(define eval (lambda (e env) (variant-case e (Const (value) value) (Var (name) (or (lookup env name) (make-Prim name))) (Lam (formal body) (make-Closure formal bodyEnvironments are now manipulated in a stack-like manner: extended before evaluating the body of a procedure and retracted after. Furthermore, there's only ever one stack. So let's eliminate theenv)) (Ap (fun arg) (let ((vfun (eval fun env)) (varg (eval arg env))) (variant-case vfun (Prim (name) (cond ((eq? name 'not) (not varg)) ...)) (Closure (formal bodyenv) (eval body (extend env formal varg)))))))))

`env`

parameter to `eval`

, make it
a global definition, and put in explicit stack operations.
(define eval (lambda (e) (variant-case e (Const (value) value) (Var (name) (or (lookup env name) (make-Prim name))) (Lam (formal body) e) (Ap (fun arg) (let ((vfun (eval fun)) (varg (eval arg))) (variant-case vfun (Prim (name) (cond ((eq? name 'not) (not varg)) ...)) (Lam (formal body) (push! formal arg) (let ((v (eval body))) (pop!) v))))))))Notice that we have also gotten rid of

`Closure`

sine they
are now the same as `Lam`

. Using the first-order
implementation of environments, the stack operations are defined as
follows:
(define env (make-empty)) (define push! (lambda (x v) (set! env (make-extend-ff env x v)))) (define pop! (lambda () (variant-case env (extend-ff (fun dom ran) (set! env fun)))))This interpreter is more efficient: it does not have to pass

`env`

around as an argument, and closures are smaller.
(More importantly, we can explicitly free the `extend-ff`

records when we `pop!`

, where we can't
do this in the original interpreter because environments don't
behave like stacks. But we haven't yet discussed memory allocation
and reclamation.)
But what is the cost of this "simplification" we have performed? Consider:

(let* ((x 0) (f (lambda (y) (add1 x))) (x 2)) (f 0))In the original interpreter (or Scheme) this expression yields the value

`1`

. Let's run
it in our new interpreter. First we have to expand `let*`

.
A: ((lambda (x) B: ((lambda (f) C: ((lambda (x) D: (f 0)) 2)) (lambda (y) (add1 x))) 0))The environment stacks at each marked point in the above program are (top of stack on left):

A: [x = 0] B: [f = (lambda (y)...)] [x = 0] C: [x = 2] [f = (lambda (y)...)] [x = 0] D: [y = 0] [x = 2] [f = (lambda (y)...)] [x = 0]The evaluation of

`(f 0)`

occurs in environment D. Here
`(lookup env x)`

yields `2`

. Thus the entire
expression evaluates to `3`

. Clearly this is not equivalent
to the original interpreter. This new interpreter implements
Recall that earlier we said that names don't matter in Scheme. This is because Scheme is lexically scoped. In Scheme, or the original interpreter, the expression

(let* ((z 0) (f (lambda (y) (add1 z))) (x 2)) (f 0))has the same value as the expression above (all we've done is to rename the first bound variable

`x`

). But evaluating this
expression in the interpreter with dynamic scoping we get
`1`

, not `3`

. With dynamic scoping, names DO
matter. Changing bound variables is not something we can
freely do.
If you think that this makes it harder to reason about programs in languages with dynamic scope, you're right. Nevertheless, for many years, Lisp systems were dynamically scoped. Emacs Lisp still is, as are many other programming languages whose designers did not understand this issue.

Implementing dynamic scoping using one global stack as above is
called ** deep binding**. An alternative way to implement
dynamic scoping is to use a separate stack for each variable. This is
called

`output-port`

, and the display routines write to that
port. You could then establish a new binding of `output-port`

just before calling P, which would be discarded when P returns.
Dynamic assignment is a way to accommodate bindings with
dynamic extent into a lexically scoped language. It is quite
simple: make a copy of the current value, change the variable
to the new value, call P, restore the old value.
**Read**: EOPL 5.7.2.

- EOPL 5.7

`set!`

with environments that map names to boxes. In addition,
we will put the result of `eval`

in a box.
The new boxes (and unboxing operations) are highlighted below:
(define eval (lambda (e env) (variant-case e (Const (value) (This new interpreter behaves the same as without the new boxes. Furthermore, we can eliminate theboxvalue)) (Var (name) (box(unbox (lookup env name)))) (Lam (formal body) (box(make-Closure formal body env))) (Ap (fun arg) (let* ((vfun (unbox(eval fun env))) (varg (unbox(eval arg env)))) (variant-case vfun (Closure (formal body arg) (eval body (extend env formal (box varg))))))) (Set! (name body) (set-box! (lookup env name) (unbox(eval body env))) (box#f)) (Let (name binding body) (box(eval body (extend env name (box (unbox(eval body env))))))))))

`(box (unbox ...))`

around `(lookup env name)`

in `Var`

without changing anything. We'll call this interpreter B.
**Exercise**: Verify that interpreter B behaves the same as
the original interpreter without boxed results.

`Ap`

the result of `(eval arg env)`

is unboxed, only
to be immediately boxed again when extending the environment
for the call. Suppose we "simplify" this:
(define eval (lambda (e env) (variant-case e (Const (value) (Now consider the following expression:boxvalue)) (Var (name) (lookup env name)) (Lam (formal body) (box(make-Closure formal body env))) (Ap (fun arg) (let* ((vfun (unbox(eval fun env))) (varg (eval arg env))) (variant-case vfun (Closure (formal body arg) (eval body (extend env formal varg)))))) (Set! (name body) (set-box! (lookup env name) (unbox(eval body env))) (box#f)) (Let (name binding body) (box(eval body (extend env name (box (unbox(eval body env))))))))))

(let ((x 1) (f (lambda (a) (begin (set! a 2) a)))) (begin (f x) (display x)))In the original interpreter (or Scheme), this expression displays

`1`

. But in the interpreter above, it displays
`2`

. What's going on?
This new interpreter uses a different parameter passing
convention known as call-by-reference. In contrast, Scheme
uses call-by-value. Under call-by-reference, every value
"lives" in some location. A procedure call passes the
location of the argument value (or a reference to the value),
rather than the value itself. When the argument is a variable,
this variable's value can be modified by changing the value of
the corresponding formal parameter, because both the argument
value and the formal parameter are bound to the same location.
In the example above, the argument `x`

and
the formal parameter `a`

are bound to the same
location, so the assignment to `a`

changes
the value of `x`

.

Fortran is an example of a language that uses call-by-reference. Call-by-reference makes it much more difficult to reason about programs. Let us look at a few more examples:

(define f (lambda (a b) (set! a (+ a b)) (set! a (+ a b)) a)) (let ((x 1) (y 1)) (f x y)) (let ((z 1)) (f z z))With call-by-value,

`f`

returns
`a + 2b`

for any arguments. Hence the two
let-expressions yield 3 and 3 respectively.
But with call-by-reference, the first
let-expression yields 3, and the second yields 4.
The difference arises because in the second call the two
formal parameters of `f`

share the same location.
When two formal parameters share the same location they
are said to Another example:

(let ((f (lambda (a) (set! (add1 a))))) (f 1))A call to

`f`

increments the formal parameter
`a`

. But here the argument is a constant, which lives in
some location. So the value in the constant's location is changed!
Since our interpreter constructs a new box each time it evaluates a
constant, this doesn't matter. But most compilers "optimize"
evaluation of constants by constructing a single box for a particular
constant at compile time, and reusing the box every time the constant
is encountered. Then, with the code above in a loop, the next time
the constant `1`

is encountered its box will have the value
`2`

. You can actually do this in many Fortran compilers.
A final example:

(define swap (lambda (a b) (let ((temp a)) (set! a b) (set! b temp)))) (let ((x 1) (y 2)) (swap x y))With call-by-value, calling

`swap`

has no effect.
But with call-by-reference, `swap`

does what its
name suggests: swaps the values of `x`

and `y`

.
(Ap (fun arg) (let* ((vfun (unbox (eval fun env))) (varg (eval arg env))) (variant-case vfun (Closure (formal body env) (let* ((b (box (unbox varg))) ; copy in (v (eval body (extend env formal b)))) (set-box! varg (unbox b)) ; copy out v)))))

- boxes
- vectors
- pairs
- arrays

Typically languages use a mix of these conventions, based on the type of
each arg. Let's add boxes to the original call-by-value
interpreter represented as pairs with cdr `#f`

.

(variant-case vfun (Prim (name) (case name (make-box (cons varg) #f) (unbox (car varg)) (set-box! (set! ...)))) (Closure (formal body env) (eval body (extend env formal (box varg)))))Boxes are passed by reference, but other values by value. So where is the code to select the parameter passing based on type? It is done for us by Scheme, since since we are representing boxes as pairs, and Scheme passes pairs by reference.

Let's add pass-by-value pairs. Their operations are:

- vcons
- vcar
- vcdr
- vset-cdr!
- vset-car!

(variant-case vfun (Closure (formal body env) (eval body (extend env formal (box (if (vpair? varg) (vcons (vcar varg) (vcdr varg)) (varg)))))))Observe that when a vpair is passed to a function,

`vset-car!`

and `vset-cdr!`

operations on that pair - Modify interpreter to use shallow binding.

- EOPL 6.1, 6.2, 6.3, 6.4