3.2 The Environment Model of Evaluation
- Recall the substitution model:
To apply a compound procedure to arguments, evaluate the body of the procedure with each formal parameter replaced by the corresponding argument. (Section 3.2)
- This is no longer adequate once we allow assignment.
- Variables are no longer merely names for values; rather, a variable designates a “place” in which values can be stored.
- These places will be maintained in structures called environments.
- An environment is a sequence of frames. A frame is a table of bindings. A binding associates a variable name with one value.
- Each frame also has a pointer to its enclosing environment, unless it is considered to be global.
- The value of a variable with respect to an environment is the value given by the binding in the first frame of the environment that contains a binding for that variable.
- If no frame in the sequence specifies a binding for the variable, then the variable is unbound in the environment.
- A binding shadows another (of the same variable) if the other is in a frame that is further in the sequence.
- The environment determines the context in which an expression should be evaluated. An expression has no meaning otherwise.
- The global environment consists of a single frame, and it is implicitly used in interactions with the interpreter.
# 3.2.1 The Rules for Evaluation
- To evaluate a combination:
- Evaluate the subexpressions of the combination.
- Apply the value of the operator subexpression to the values of the operand subexpressions.
- The environment model redefines the meaning of “apply.”
- A procedure is created by evaluating a λ-expression relative to a given environment.
- The resulting procedure object is a pair consisting of the text of the λ-expression and a pointer to the environment in which the procedure was created.
- To apply a procedure to arguments, create a new environment whose frame binds the parameters to the values of the arguments and whose enclosing environment is specified by the procedure.
- Then, within the new environment, evaluate the procedure body.
(lambda (x) (* x x))
evaluates a to pair: the parameters and the procedure body as one item, and a pointer to the global environment as the other.(define square (lambda (x) (* x x)))
associates the symbolsquare
with that procedure object in the global frame.- Evaluating
(define var val)
creates a binding in the current environment frame to associatevar
withval
. - Evaluating
(set! var val)
locates the binding ofvar
in the current environment (the first frame that has a binding for it) and changes the bound value toval
. - We use
define
for variables that are currently unbound, andset!
for variables that are already bound.
# 3.2.2 Applying Simple Procedures
- Let’s evaluate
(f 5)
, given the following procedures:
(define (square x) (* x x))
(define (sum-of-squares x y) (+ (square x) (square y)))
(define (f a) (sum-of-squares (+ a 1) (* a 2)))
- These definitions create bindings for
square
,sum-of-squares
, andf
in the global frame. - To evaluate
(f 5)
, we create an environment with a frame containing a single binding, associatinga
with5
. - In we evaluate
(sum-of-squares (+ a 1) (* a 2))
. - We must evaluate the subexpressions of this combination.
- We find the value associated with
sum-of-squares
not in but in the global environment. - Evaluating the operand subexpressions yields
6
and10
. - Now we create with a frame containing two bindings:
x
is bound to6
, andy
is bound to10
. - In we evaluate
(+ (square x) (square y))
. - The process continues recursively. We end up with
(+ 36 100)
, which evaluates to136
.
# 3.2.3 Frames as the Repository of Local State
- Now we can see how the environment model makes sense of assignment and local state.
- Consider the “withdrawal processor”:
(define (make-withdraw balance)
(lambda (amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds")))
- This places a single binding in the global environment frame.
- Consider now
(define W1 (make-withdraw 100))
.- We set up where
100
is bound to the formal parameterbalance
, and then we evaluate the body ofmake-withdraw
. - This returns a lambda procedure whose environment is and this is then bound to
W1
in the global frame.
- We set up where
- Now, we apply this procedure:
(W1 50)
.- We construct a frame in that binds
amount
to50
, and then we evaluate the body ofW1
. - The enclosing environment of is not the global environment.
- Evaluating the body results in the
set!
rebindingbalance
in to the value(- 100 50)
, which is50
. - After calling
W1
, the environment is irrelevant because nothing points to it. - Each call to
W1
creates a new environment to holdamount
, but uses the same (which holdsbalance
).
- We construct a frame in that binds
(define W2 (make-withdraw 100))
creates another environment with abalance
binding.- This is independent from which is why the
W2
object and its local state is independent fromW1
. - On the other hand,
W1
andW2
share the same code.
- This is independent from which is why the
# 3.2.4 Internal Definitions
- With block structure, we nested definitions using
define
to avoid exposing helper procedures. - Internal definitions work according to the environmental model.
- When we apply a procedure that has internal definitions, there are
define
forms at the beginning of the body. - We are in so evaluating these adds bindings to the first frame of right after the arguments.
- When we apply the internal procedures, the formal parameter environment is created, and its enclosing environment is because that was where the procedure was defined.
- This means each internal procedure has access to the arguments of the procedure they are defined within.
- The names of local procedures don’t interfere with names external to the enclosing procedure, due to