CSCI 2041

ADVANCED PROGRAMMING PRINCIPLES

Lazy Evaluation:

lazy_t

Last time: manipulating infinite data structures in lazyCaml

let rec nats n = n::(nats (n+1))
let rec trues = true::trues

One way to implement these in OCaml is with a stream type, in which the tail of a stream is placed in a lambda expression to prevent eager evaluation:

type 'a stream = End | Cons of 'a * (unit -> 'a stream)

But streams do not implement lazy evaluation…

Technically, streams simulate normal order evaluation (call-by-name) but not lazy evaluation, since there is no sharing…

let rec natp n =
  let _ = printf "evaluated natp %d\n" n in
  Cons(n, fun () -> natp (n+1));;

let double s = merge s s;;
# take_s 6 (double (natp 10))
evaluated natp 10
evaluated natp 11
evaluated natp 11
evaluated natp 12
evaluated natp 12
evaluated natp 13
evaluated natp 13
- : int list = [10; 10; 11; 11; 12; 12]

OCaml has a built-in type ('a lazy_t) for true lazy evaluation:

lazy : 'a -> 'a lazy_t (* lazy(e) is e wrapped for lazy evaluation *)
Lazy.force : 'a lazy_t -> 'a (* forces evaluation of a 'a lazy_t *)
type 'a lzlist = Nil | LzCons of 'a * ('a lzlist lazy_t)

let rec lznatp n =
  let _ = printf "evaluated lznatp %d\n" n in
  LzCons(n, lazy(lznatp (n+1)))

let rec lztake n ll = match (n,ll) with
| (0,_) | (_, Nil) -> []
| LzCons(h,t) -> h::(lztake (n-1) (Lazy.force t))
(* or: LzCons(h,lazy(t)) -> h::(lztake (n-1) t) *)
let rec lzmerge l1 l2 = match (l1,l2) with
| (Nil, _) -> l2
| (_,Nil) -> l1
| (LzCons(h,lazy(t)),_) -> LzCons(h, lazy(lzmerge l2 t))

This also works for previous lazyCaml examples:

let rec lz_dfs t =
  let rec dfs t rst = match t with
  | Empty -> Lazy.force rst
  | Leaf v -> LzCons(v, rst)
  | Node(l,r) -> dfs l (lazy (dfs r rst)) in
  dfs t (lazy Nil)
let rec eq_ll l1 l2 = match (l1,l2) with
| (Nil, Nil) -> true
| (Nil, _) | (_, Nil) -> false
| (LzCons(h1,t1),LzCons(h2,t2)) ->
  (h1=h2) && (eq_ll (Lazy.force t1) (Lazy.force t2))
let t1 = Node(Leaf 7, Node(Leaf 3, Leaf 11))
let l1 = lz_dfs t1

let t2 = Node(Leaf 3, Node(Leaf 7, Leaf 11))
let l2 = lz_dfs t2

eq_ll l1 l2

Let’s double check that we’re making things faster…

let rec node_chain n k =
  if n = 0 then k (Leaf 1)
  else node_chain (n-1) (fun r -> k (Node (r, Empty)))

let bigN = 1 lsl 24 (* 2 to the power 25, about 33M *)
let t4 = Node(Leaf 4, node_chain bigN Fun.id)
let l4 = lz_dfs t4
eq_ll l2 l4

cs2041.org

// reveal.js plugins