Exercise 4.30

Read Exercise 4.30 ~ Solution


For those following along – we saw in Exercise 4.14 that the evaluator should not use the built-in higher order procedures like for-each and map. At the time, I should have removed them from the set-up of the initial environment but I didn’t. Now that we are looking at the mechanics of for-each though I have finally removed them.

(run-interpreter '(define (for-each proc items)
                    (if (null? items)
                        'done
                        (begin (proc (car items))
                               (for-each proc (cdr items))))))

(run-interpreter '(for-each (lambda (x) (newline) (display x))
                            (list 57 321 88)))

(run-interpreter '(define (p1 x)
                    (set! x (cons x '(2)))
                    x))

(run-interpreter '(define (p2 x)
                    (define (p e)
                      e
                      x)
                    (p (set! x (cons x '(2))))))
(run-interpreter '(p1 1))
(run-interpreter '(p2 1))

a

Ben’s version of eval-sequence works correctly in this example for two reasons. One is that there is no mutation in the sequence of expressions – neither in the body of for-each, nor the lambda that is passed as an argument. The other reason is that the lambda expression contains a primitive procedure, display, which causes its argument to be forced, so despite eval-sequence creating thunk values, those values are forced when display is evaluated.

b

In the original evaluator:
(p1 1) => (1 2)
(p2 1) => 1
In Cy’s evaluator:
(p1 1) => (1 2)
(p2 1) => (1 2)
The reason is that p is a compound procedure and so eval-sequence is called to evaluate it. The argument e is bound to a thunk of the set! expression. In Cy’s version of the evaluator, each expression’s actual value is evaluated, forcing the value of the thunk and mutating the value of x. In the original evaluator eval is used to evaluate e. Since e is a thunk that eval does not force, the set! expression is not evaluated and the value of x is not mutated.

c

Cy’s evaluator works identically to the original evaluator in example a because the lambda has the primitive procedure display which always causes any thunk to be forced.

d

On the one hand, the behaviour of Cy’s evaluator is much more intuitive, particularly if we’re dealing with lots of mutation. However, if we go to the trouble of writing a lazy evaluator, I don’t think there should be concessions to intuition; the whole point is to explore a different paradigm. In this case I prefer the original evaluator and embrace the stimulation of a new mind-set brought about by pure laziness.

Leave a comment