One way to define laziness is as follows: a language has strict evaluation if and only if \(f(\bot) = \bot\) for every function \(f\) definable in the language.
We can use this definition to solve another problem: comparing algorithms across lazy and strict languages. In particular, it allows us to ask questions like "how many superfluous computations does this traversal perform in a strict language?"
We can form a xiphophyllous tree from an algebraic data type by simply calling
its constructors with undefined
as an argument, viz.
import Data.List (partition)
evens :: [a] -> [a]
evens = fmap snd . fst . partition (even . fst) . zip [0..]
list :: [String]
list = ["a", undefined, "b", undefined, "c", undefined, "d", undefined]
You can then verify that evens list
evaluates to ["a", "b", "c", "d"]
, as
desired. We say that the application of evens
to list
is xiphophyllous of degree
\(\frac{1}{2}\); this function will visit half as many leaves in a lazy
language as it would in a strict one.
In fact, this generalizes to arbitrary inductively defined data types; when the number of nodes that can be ignored is finite, the minimum of such is well-defined.