Let's take a simple example of a fib
function as an example and try to understand thunks in haskell!
fib
function implements Fibonacci seq.
1 2 3 4 

In most of the programming langauges that supports tuples, saying (4+2,7)
would be stored as (6,7)
but in Haskell it gets stored as (_,7)
where the _
is the thunk value.
Lets explore the same in ghci:
1 2 3 4 

Notice that _t1
is the thunk value here, now if we fetch the second element of that tuple, it will be 7, but the first element we not be evaluated yet!
1 2 3 4 5 

But as soon as we fetch the first or just evaluate x
, the first value will be evaluated.
1 2 3 4 5 

So, a thunk is basically a value that has not yet been computed yet. As per the need it can evaluate or partlyevaluated to a real value. Partial evaluation of a thunk will resulting in a value with more thunks in it.
Now the above function can be expanded as:
1 2 3 

The first row left shifted is the second row and the third row is the sum of the first and the second rows and all of them are sharing the same thunk.
take 2 fibs
will give use [0, 1]
which is direct and does not need further evaluation.
Now if we say take 3 fib
Haskell will directly get the 0 and 1, and then it will have to partly evaluate the thunk, so that it can fully evaluate zipWith (+) fib (tail fib)
, by getting the sum of the first two rows but it can't fully do that, it can begin to sum the first two rows:
1 2 3 

Likewise it keeps on going creating <thunk's>
and eval them as and when the need arrives.
If you say fib n
and then fib nm
where m<=n
the expresion will not be reevaled but will just take the first nm
numbers from the list it has already evaled!
P.S: This post is a part from my repo haskellrascal.