Reputation: 25812
type 'a list_t =
| Empty
| Node of 'a * 'a list_t lazy_t
type 'a node_t =
| Empty
| Node of 'a * 'a zlist_t
and 'a zlist_t = 'a node_t lazy_t
I don't see many differences.
The only thing I can identify is that in the 2nd
type, even the Empty
is put to a lazy thunk, am I right? If I am right, then what is the purpose of put Empty
to thunk?
Any other differences?
Edit
I am asking the differences between the two types of list: list_t
and zlist_t
.
Upvotes: 2
Views: 119
Reputation: 122449
There is absolutely no difference between 'a node_t
and 'a list_t
, since you can substitute the definition of the alias 'a zlist_t
into the definition of 'a node_t
and what you get is exactly analogous to 'a list_t
.
You said you are asking about the difference between 'a list_t
and 'a zlist_t
. Well, 'a zlist_t
is just a type alias for 'a node_t lazy_t
. As we noted above that node_t
and list_t
are equivalent, that means 'a zlist_t
, i.e. 'a node_t lazy_t
, is equivalent to 'a list_t lazy_t
. So your question is basically asking what is the difference between 'a list_t
and 'a list_t lazy_t
.
Well, from looking at it, the difference is simple -- one is the other wrapped in a lazy_t
, which means it's the lazy version of the other. If you have a value of type 'a list_t
, it means that the first cons cell is already evaluated, since it's value must either be Empty
or Node
, without evaluating lazy expressions. Further, if it is Node
, the first item must also be evaluated, since it lives inside the Node
without a lazy_t
. On the other hand, if you have a value of type 'a list_t lazy_t
, it means the first cons cell may not be evaluated; you don't get an 'a list_t
until you force it.
Upvotes: 1
Reputation: 1739
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 = ()
Upvotes: 3
Reputation: 4929
There's no difference. 'a zlist_t
is an alias for 'a node_t lazy_t
. After performing the substitution, we end up with:
type 'a node_t = | Empty | Node of 'a * 'a node_t lazy_t
What you could do is:
type 'a node_t = | Empty | Node of ('a * 'a node_t) lazy_t
Perhaps better written as:
type 'a node = | Empty | Node of 'a zlist and 'a zlist = ('a * 'a node) lazy_t
Note that since OCaml 3.08 or so we can force the evaluation of lazy expressions during pattern-matching like this:
match l with | Empty -> ... | Node (lazy (..., ...)) ->
Don't use this definition. It's better to have the list completely lazy like in your definitions. Independently you can always make the elements of the list lazy if you wish.
Upvotes: 0