# Thunks, Tasks, and Threads A `Thunk` is defined as ```lean # namespace Ex # universe u structure Thunk (α : Type u) : Type u where fn : Unit → α # end Ex ``` A `Thunk` encapsulates a computation without evaluation. That is, a `Thunk` stores the way of how the value would be computed. The Lean runtime has special support for `Thunk`s. It caches their values after they are computed for the first time. This feature is useful for implementing data structures such as lazy lists. Here is a small example using a `Thunk`. ```lean def fib : Nat → Nat | 0 => 0 | 1 => 1 | x+2 => fib (x+1) + fib x def f (c : Bool) (x : Thunk Nat) : Nat := if c then x.get else 0 def g (c : Bool) (x : Nat) : Nat := f c (Thunk.mk (fun _ => fib x)) #eval g false 1000 ``` The function `f` above uses `x.get` to evaluate the `Thunk` `x`. The expression `Thunk.mk (fun _ => fib x)` creates a `Thunk` for computing `fib x`. Note that `fib` is a very naive function for computing the Fibonacci numbers, and it would an unreasonable amount of time to compute `fib 1000`. However, our test terminates instantaneously because the `Thunk` is not evaluated when `c` is `false`. Lean has a builtin coercion from any type `a` to `Thunk a`. You write the function `g` above as ```lean # def fib : Nat → Nat # | 0 => 0 # | 1 => 1 # | x+2 => fib (x+1) + fib x # def f (c : Bool) (x : Thunk Nat) : Nat := # if c then # x.get # else # 0 def g (c : Bool) (x : Nat) : Nat := f c (fib x) #eval g false 1000 ``` In the following example, we use the macro `dbg_trace` to demonstrate that the Lean runtime caches the value computed by a `Thunk`. We remark that the macro `dbg_trace` should be used for debugging purposes only. ```lean def add1 (x : Nat) : Nat := dbg_trace "add1: {x}" x + 1 def double (x : Thunk Nat) : Nat := x.get + x.get def triple (x : Thunk Nat) : Nat := double x + x.get def test (x : Nat) : Nat := triple (add1 x) #eval test 2 -- add1: 2 -- 9 ``` Note that the message `add1: 2` is printed only once. Now, consider the same example using `Unit -> Nat` instead of `Thunk Nat`. ```lean def add1 (x : Nat) : Nat := dbg_trace "add1: {x}" x + 1 def double (x : Unit -> Nat) : Nat := x () + x () def triple (x : Unit -> Nat) : Nat := double x + x () def test (x : Nat) : Nat := triple (fun _ => add1 x) #eval test 2 -- add1: 2 -- add1: 2 -- 9 ``` Now, the message `add1: 2` is printed twice. It may come as a surprise that it was printed twice instead of three times. As we pointed out, `dbg_trace` is a macro used for debugging purposes only, and `add1` is still considered to be a pure function. The Lean compiler performs common subexpression elimination when compiling `double`, and the produced code for `double` executes `x ()` only once instead of twice. This transformation is safe because `x : Unit -> Nat` is pure.