Frames as the Repository of Local State

We can turn to the environment model to see how procedures and
assignment can be used to represent objects with local state. As an
example, consider the ``withdrawal processor'' from
section created by calling the
procedure

Figure shows the result of defining the
make-withdraw procedure in the global environment. This produces a
procedure object that contains a pointer to the global environment.
So far, this is no different from the examples we have already seen,
except that the body of the procedure is itself a lambda
expression.

The interesting part of the computation happens when we apply the
procedure make-withdraw to an argument:

(define W1 (make-withdraw 100))

We begin, as usual, by setting up an environment E1 in which the
formal parameter balance is bound to the argument 100. Within
this environment, we evaluate the body of make-withdraw, namely
the lambda expression. This constructs a new procedure object,
whose code is as specified by the lambda and whose environment
is E1, the environment in which the lambda was evaluated to
produce the procedure. The resulting procedure object is the value
returned by the call to make-withdraw. This is bound to
W1 in the global environment, since the define itself is being
evaluated in the global environment. Figure shows the
resulting environment structure.

Now we can analyze what happens when W1 is applied to an
argument:

(W1 50)
50

We begin by constructing a frame in which amount, the formal
parameter of W1, is bound to the argument 50. The crucial point
to observe is that this frame has as its enclosing environment not the
global environment, but rather the environment E1, because this is the
environment that is specified by the W1 procedure object.
Within this new environment, we evaluate the body of the procedure:

The resulting environment structure is shown in
figure . The expression being evaluated references
both amount and balance. Amount will be found in
the first frame in the environment, while balance will be found
by following the enclosing-environment pointer to E1.

When the set! is executed, the binding of
balance in E1 is changed. At the completion of the call to W1,
balance is 50, and the frame that contains balance
is still pointed to by the procedure object W1. The frame
that binds amount
(in which we executed the code that changed balance) is no longer
relevant, since the procedure call that constructed it has terminated,
and there are no pointers to that frame from other parts of the
environment. The next time W1 is called, this will build a new
frame that binds amount and whose enclosing environment is E1.
We see that E1 serves as the ``place'' that holds the local state
variable for the procedure object W1. Figure
shows the situation after the call to W1.

Observe what happens when we create a second ``withdraw'' object by
making another call to make-withdraw:

(define W2 (make-withdraw 100))

This produces the environment structure of figure , which shows
that W2 is a procedure object, that is, a pair with some code
and an environment. The environment E2 for W2 was created by
the call to make-withdraw. It contains a frame with its own
local binding for balance. On the other hand, W1 and
W2 have the same code: the code specified by the lambda
expression in the body of make-withdraw. We see here why W1 and W2
behave as independent objects. Calls to W1 reference the state
variable balance stored in E1, whereas calls to W2
reference the balance stored in E2. Thus, changes to the local
state of one object do not affect the other object.

Exercise.
In the make-withdraw procedure, the local variable balance
is created as a parameter of make-withdraw. We could also
create the local state variable explicitly, using let, as
follows: