The following function computes the Fibonacci sequence:
fib = 0 : 1 : (zipWith (+) fib (tail fib))
If we run it, we will get an infinite list, but how does the recursion work? Why does it get to print numbers on the screen if it the function keeps calling itself? I would appreciate if you could explain how the compiler manages the calls.
I've drawn a picture, which you might find helpful.
Note that
zipWtih op (x:xs) (y:xs) = (op x y):zipWith xs ys
, which is howzipWtih
appears to "move" right along the list. It's reading elements and spitting out sums:Here's a more detailed step-by-step evaluation. (Although I'll paste copies of what's there, there's only one copy in memory.) I'll use
....
for things I can't be bothered to write out.notice that now we know that
zipWith (+) fib (tail fib) = 1:.....
.I'll go a little faster:
At each stage, the last two arguments to the
zipWith
function are like pointers to (one and two positions) further up thefib
list than we are at present.In a word: laziness. A list in Haskell is more like a generator: it will only compute values when they are demanded by something else.
For instance
head [1 , 2+3]
will not perform the addition, since it is not needed. Similarly, if we recursively letones = 1 : ones
, thenhead ones = head (1 : ones) = 1
does not need to evaluate all the tail.You can try guessing what happens if we print a pair
x
, defined as follows:Above we use a (lazy) pair instead of a (lazy) list, but the reasoning is the same. Don't evaluate anything unless is it needed by something else.