The problem with the first one, I think, is that Node (foo, lazy Empty)
cannot be wrapped in a Lazy.t
to make the evaluation of foo
itself lazy.
Let me explain a little bit more with two examples :
# type 'a list_t = Empty | Node of 'a * 'a list_t lazy_t;;
type 'a list_t = Empty | Node of 'a * 'a list_t lazy_t
# Node ((print_endline "hello"; 1), lazy Empty);;
hello
- : int list_t = Node (1, lazy Empty)
Here, the evaluation of the 'a
element has been performed before I could build the list. In other words, the first type definition you wrote is a definition for lists whose head element has always been evaluated. Conversely, the second definition does not force you to evaluate the head of the list in order to build it.
# type 'a node_t = Empty | Node of 'a * 'a zlist_t and 'a zlist_t = 'a node_t lazy_t;;
type 'a node_t = Empty | Node of 'a * 'a zlist_t
and 'a zlist_t = 'a node_t lazy_t
# let x = ((lazy (Node ((print_endline "hello"; 1), lazy Empty))): int zlist_t);;
val x : int zlist_t = <lazy>
The evaluation of the element of type 'a
contained in the head of the list takes place when you first force the list, for instance as follows:
# match Lazy.force x with Empty -> () | Node _ -> ();;
hello
- : unit = ()