This chapter in SICP says that the definition of actual-value
for extracting a thunk's real value is this:
(define (actual-value exp env)
(force-it (eval exp env)))
But what if exp
itself is a thunk? Based on the definition of delay-it
it would mean that it is a list object of the form (list 'thunk exp env)
. The eval function however is in no way prepared for handling tagged lists beginning with 'thunk. Why doesn't eval produce an error due to the unmatched cond expression?
Edit: I think evaluating the following expression should result in an error:
(define (add a) (+ 2 a))
(add 0)
add
is a compound procedure, therefore delay-it
is performed on its arguments before it being applied. +
is a primitive produce, which means that actual-value
will be called on its arguments. The arguments are 2 and a. a is a thunk object, therefore actual-value
should produce an error when it passes it to eval
, because eval
does not have a cond case which deals with lists tagged with 'thunk.
The key point here is that when we're evaluating (+ 2 a)
the a
is not a thunk, it's just a symbol that will be looked up in the environment, and whose value is a thunk. And after eval
returns the thunk, force-it
will take care of forcing its value. Let's step through the process.
The only argument that gets delayed in your example is the 0
, at the time of invoking add
, but even so that value will get forced eventually by list-of-arg-values
- think of it, at some point all the procedure applications will lead to apply-primitive-procedure
, and that's the point where we force the thunks.
If we perform a trace at the time of calling list-of-arg-values
, the values passed to actual-value
are 2
and a
in that order. The 2
just evaluates to itself, no problem there. Let's see what happens with the a
; this snippet in actual-value
:
(eval exp env)
... will receive the symbol a
as its exp
(a variable), returning the associated thunk after looking it up in the environment (remember: when we extended the environment in apply
, we created bindings between variables and thunks), and after that force-it
will receive a thunk as the result of calling eval
:
(force-it (eval exp env)))
... and force-it
knows how to evaluate the thunk, in the (thunk? obj)
case. And that's it! finally we obtain 0
, the actual value.