Skip to content
Part IA Michaelmas Term

Functionals for Lazy Lists

filterq: lazy filtering

let rec filterq p = function
  | Nil -> Nil
  | Cons (x, xf) ->
      if p x then
        Cons (x, fun () -> filterq p (xf ()))
      else
        filterq p (xf ())
(* val filterq : ('a -> bool) -> 'a seq -> 'a seq = <fun> *)

filterq demands elements from the sequence until it finds one satisfying p. It contains a force (xf()) not protected by a delay in the else branch: if no element satisfies the predicate within a finite prefix, filterq will evaluate the rest before producing any output.

Behaviour with infinite sequences

If the input sequence is infinite and contains no element satisfying p, then filterq runs forever without producing any output. This is a natural consequence: the function cannot know that no matching element exists without examining every element, of which there are infinitely many.

iterates: generalised from

let rec iterates f x =
  Cons (x, fun () -> iterates f (f x))
(* val iterates : ('a -> 'a) -> 'a -> 'a seq = <fun> *)

iterates f x generates the infinite sequence:

x, f(x), f(f(x)), f(f(f(x))), …

This generalises from: from k is equivalent to iterates (fun n -> n + 1) k.

Exercise: map analogue for sequences

A lazy map functional would apply a function to every element of a sequence on demand. The key is to delay the recursive call:

let rec mapq f = function
  | Nil -> Nil
  | Cons (x, xf) -> Cons (f x, fun () -> mapq f (xf ()))

The fun () -> around the recursive call ensures that the tail is computed only when forced by the consumer.