Skip to content
Lazy Values
step 1/5

Reading — step 1 of 5

Learn

~2 min readLazy Evaluation and Sequences

OCaml is strict by default (eager evaluation), but you can opt into laziness with lazy.

let expensive = lazy (
    print_endline "computing...";
    1 + 1
)

(* Not computed yet *)

let () = print_endline (string_of_int (Lazy.force expensive))
(* prints "computing..." then "2" *)

let () = print_endline (string_of_int (Lazy.force expensive))
(* prints "2" only — cached after first force *)

Lazy.force evaluates if needed; subsequent forces return the cached value.

'a lazy_t is the type. Pattern match with lazy:

let rec take n (lazy seq) =
    if n = 0 then []
    else match seq with
        | Cons (x, rest) -> x :: take (n - 1) rest
        | Nil -> []

Building a lazy sequence type:

type 'a stream = Cons of 'a * 'a stream Lazy.t | Nil

(* Generate naturals 1, 2, 3, ... lazily *)
let rec naturals_from n = Cons (n, lazy (naturals_from (n + 1)))

let take n s =
    let rec aux n s acc =
        if n = 0 then List.rev acc
        else match s with
            | Nil -> List.rev acc
            | Cons (x, rest) -> aux (n - 1) (Lazy.force rest) (x :: acc)
    in
    aux n s []

let first_5 = take 5 (naturals_from 1)
(* [1; 2; 3; 4; 5] — only computes 5 elements *)

Seq module in stdlib gives you this for free:

let rec naturals_from n =
    Seq.cons n (fun () -> Seq.uncons (naturals_from (n + 1)) |> Option.get |> snd)

(* Cleaner with Seq combinators *)
let first_5 = Seq.unfold (fun n -> Some (n, n + 1)) 1 |> Seq.take 5 |> List.of_seq

Seq.t is unit -> 'a node where node is Cons of 'a * 'a t | Nil. Lazy by construction.

Use cases:

  • Infinite sequences (primes, Fibonacci, Pi digits)
  • Streaming over big files
  • Memoization
  • Building computation graphs to evaluate later

Most OCaml code is strict. Reach for lazy when you really need to defer work.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…