WebSTG Docs

Fibonacci laziness

In this example, we will examine the lazy behaviour of the zipWith implementation for the fibonacci sequence. You can also open this for yourself in the simulator.

Note: Figures 6 and 7 are not marked in the simulator, but are 1 step further than figures 4 and 5, respectively.

The basic idea of the implementation is using zipWith to traverse the sequence and generate the next element when necessary. In the STG language, this could be implemented like this:

stg fib zipWith
zero = CON(Num 0)
one = CON(Num 1)
-- let main = 0 : 1 : zipwith (+) fib (tail fib)
main = THUNK(letrec	fib0 = CON(Cons zero fib1)
					fib1 = CON(Cons one fib2)
					fib2 = THUNK(zipWith plusInt fib0 fib1)
			in fib0)

Initial state

As main is entered, the initial list is created, and a thunk is created for the zipWith call.

Heap

Figure 1

Expanding the sequence

When the tail of 0x7 is entered, zipWith executes and allocates thunks on the heap, corresponding to the next element (0x9) and the spine (0xa) of the sequence.

Heap

Figure 2

Expanding it further

Note that at this point, the calculation of the next element still hasn’t been performed. In fact, as long as we are following the spine of the sequence, no element will be calculated.

Heap

Figure 3

Triggering the calculations

When we stop following the spine and enter one of the elements, the calculation(s) will finally be performed, and the thunk(s) will be updated. Entering an element is effectively calling the plusInt function:

stg plusInt
plusInt = FUN(x y -> 
	case x of {
	Num i -> case y of {
			Num j -> case  i +# j of {
						x -> let result = CON(Num x)
							in result;
					};
			};
	}
)

Now we can check out what is happening with the stack. When we entered 0xf an update frame was pushed on, and plusInt started executing with x=0x9 and y=0xc.
Before plusInt starts to scrutinize x, it must save any other locals it still needs, and pushes them onto the stack alongside the case continuation (figure 4).
A similar thing happens when y becomes the scrutinee; i must be saved with the case continuation, and y is entered (figure 5).

Stack

START
Update0xf
Case continuation
Saved locals:y 0xc
Update0x9
Figure 4

Stack

START
Update0xf
Case continuation
Saved locals:i 1
Update0xc
Figure 5

Heap

Figure 6: 0x9 is updated by an indirection to 0x12.

Heap

Figure 7: 0xc is updated by an indirection to 0x13

Heap

Figure 8: 0xf is updated by an indirection to 0x14