The Magic of Thunk
A thunk is simply a function with the unit parameter. For example:
let f() = 1 + 2 * 3;;
It is indeed a function and seems simple enough. However, don't overlook it. Not having any parameters makes it so special that it even has such a particular name, thunk. Let's have a look at its features.
If a function has parameters, the result of its application may depend on its arguments. For example:
let g x y = x + y;;
Without giving values for x and y, we won't be able to know what concrete value that g would give back to us.
Thunk, however, does not have any parameters, which means its result must be fixed. After defining it, the body will not be affected by anything any more. It is like a sealed box: we may not know what's inside yet, but we know that once it is there, its content won't change. For the
f at the beginning, we know its value will definitely be 7. For
let f1() = h 2 (h 3 4), we may not know the value of its body but we are sure its body is two applications of
h and it won't change.
Binding also has determined result and it is immediately evaluated after we define it. For example,
let x = 1 + 2 * 3 will return you x with 7 at once.
Thunk is different. When we hold a thunk, we must call it like
f() to get its result. The evaluation is a kind of delayed, but we have the full control. Here is a demonstration:
(*Will fail immediately after you hit enter*) let y = 10 / 0;; (*Will not fail but return a thunk*) let f_div0() = 10 / 0;; (*Now fail*) f_div0();;
Perfect computation capsule
With the help of the above two features, thunk encapsulates computations without evaluation, in other words, thunk does not store the actual value; instead, it stores the way of how the value would be computed. And the actual compuations will be carried out only when you decide so. Here is a case that desires thunk:
let print_prime prime x = if x then print_int prime else print_int 0;; (*Assume find_nth_prime n is already somewhere*) print_prime (find_nth_prime 1000000) false;;
print_prime prints the prime only when x is true. It sounds ok. But the next function application shows a flaw that it will not print the 1,000,000th prime, but anyway the prime will be computed. What a waste of cpu cycles!
With thunk, it gets better:
let print_prime prime x = if x then print_int (prime()) (*called when needed*) else print_int 0;; (*Create a thunk*) let millionth_prime() = find_nth_prime 1000000;; print_prime millionth_prime false;;
print_prime takes a thunk and we also create a capsule for the missionth prime. The thunk will be evaluated only before printing.
Thunk is the fundamental element for several classic usages:
- stream_list: element is produced when being retrieved
- lazy: evaluated only when needed and only once
- async: concurrent computing, a kind of queue with lots scheduled computations.
The above three and the important roles of thunks will be presented in details in future posts.
To be continued