Lecture 019

Lazy Evaluation / Streams

Demand driven computation suspension: stop before next request laziness: can do more work if exposed multiple times, but memorlization can solve the issue

Infinite Data Structure

signature STREAM =
sig
  type 'a stream
  datatype 'a front = Empty | Cons of 'a * 'a stream
  val expose: 'a stream -> 'a front
  val delay: (unit -> 'a front) -> 'a stream
  val empty: 'a stream
  val cons: 'a * 'a stream -> 'a stream
  val null: 'a stream -> bool
  val map: ('a * 'b) -> 'a stream -> 'b stream
  val filter: ('a -> bool) -> 'a stream -> 'a stream
  val take: 'a stream * int -> 'a list
end

structure Stream :> STREAM =
struct
  datatype 'a stream = STREAM of unit -> 'a front
  and 'a front = EMPTY | Con of 'a * 'a stream
  val empty = STREAM (fn () => Empty)
  fun expose (STREAM d) = d ()
  fun delay d = STREAM d
  fun cons (x, rest) = STREAM (fn () => Cons(x, rest))
  fun null s = (case expose s of Empty => true | Cons _ => false)
  (* This will do a lot of work because expose calls the stream *)

  val natureNumber : int Stream.stream = ...
  val negativeNumber = Stream.filter (fn n => n < 0) natureNumber
  (*negativeNumber is empty, not button, but takes forever to evaluate null because filter happens when we try to see if there is an element*)

  fun map' f Empty = Empty
    | map' f (Cons(x, s)) = cons (f x, map f s)
  and map f s = delay (fn () => map' f (expose s))

  fun filter p s = delay (fn () => filter' p (expose s))
  and filter' p Empty = Empty
    | filter' p (Cons(x, s)) = if p x then Cons(x, filter p s) else filter' p (expose s)

  fun append (s1, s2) = delay (fn () => append' (expose s1, s2))
  and append' (Empty, s2) = expose s2
    | append' (Cons(x, s1), s2) = Cons(x, append (s1, s2))

end

Examples:

fun ones' () = Stream.cons (1, Stream.delay ones')
val ones = Stream.delay one'

fun natsfrom' n = Stream.cons(n, S.delay (fn () => natsfrom' (n+1)))
fun natsfrom n = Stream.delay(fn () => natsfrom' n)
val nats = natsfrom 0

or...

fun natsfrom' n = Stream.cons(n, natsfrom (n+1))
and natsfrom n = Stream.delay (fn () => natsfrom 'n)
val nats = natsfrom 0
val Stream.Con(x0, r) = expose nats [0/x0, -/r]

Example of finding primes:

(*whether q is divisable by p*)
fun notdiv p q = q mod p <> 0
fun sieve s = Stream.delay (fn () -> sieve' (Stream.expose s))
and sieve' Stream.Empty = Stream.Empty
  | sieve' (Stream.Cons(p, s)) = Stream.cons(p, sieve (Stream.filter(notdiv p) s))

val primes = sieve (natsfrom 2)

Table of Content