In the previous subsection, we learned that we can no longer use the substitution model of evaluation once we use assignments. The new model that will be used from now on is called the environment model of evaluation.
Let's go through the example to see how this new model works. We define a simple square
procedure and call it on 7
:
> (define (square x) (* x x))
square
> (square 7)
49
What happens? The substitution model states:
In this example, the substitution of 7
for x
in (* x x)
gives (* 7 7)
. In step 2 we evaluate that expression to get the result, 49
.
Now, let's put the substitution model aside and take a look at the more complete and comprehensive environment model:
A frame is a collection of name-value associations, or bindings. In our example, the frame has one binding that binds x
to 7
.
Let's skip step 2 for a moment and think about step 3. The idea is that we are going to evaluate the expression (* x x)
, but we are refining our notion of what it means to "evaluate" an expression. Expressions are no longer evaluated in a vacuum, but instead, every evaluation must be done with respect to some
environment.
An environment can be described as some collection of bindings between names and values. When we are evaluating (* x x)
and we see the symbol x
, we want to be able to look up x
in our collection of bindings and find the value 7
. Looking up the value bound to a symbol is something we've done before with global variables. What's new is that instead of one central collection of bindings we now have the possibility of local environments. The symbol x
isn't always 7
. That's only the case during this one invocation of square
. So, step 3 means to evaluate the expression in the way that we've always understood, but looking up names in a particular place.
What's step 2 about? The point is that we can't evaluate (* x x)
in an environment with nothing but the x
to 7
binding, because we also have to look up a value for the symbol *
(namely, the multiplication function). So, we create a new frame in step 1, but that frame isn't an environment by itself. Instead we use the new frame to extend an environment that already existed.
Which old environment do we extend? In the square
example, there is only one
candidate, the global environment. But in more complicated situations there
may be several environments available.
Now, we will go over the rules for the environment model for different cases. Before we proceed, keep in mind that:
Let's get some perspective on how we expression atomic values:
#t
, and #f
are self-evaluating.What about procedures? How does the evaluation deal with expressions that invoke procedures?
Apply the procedure (the value of the first subexpression) to the arguments (the values of the other subexpressions).
If the procedure is compound (user-defined):
If the procedure is primitive:
(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)))
Procedure objects in the global frame.
Environments created by evaluating (f 5)
.
lambda
creates a procedure in the form of a double bubble. The left circle points to the text of the lambda
expression; the right circle points to the defining environment (i.e., to the current environment at the time the lambda
is seen). ONLY LAMBDAS CREATE PROCEDURES.define
adds a new binding to the current frame.set!
changes the first available binding.let
is a lambda
with an invocation.(define (...) …)
= lambda
+ define
cond
, if
).(define (make-withdraw balance)
(lambda (amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds")))
Result of defining make-withdraw
in the global environment.
Result of evaluating (define W1 (make-withdraw 100))
.
In this subsection, we learned how to evaluate procedures with the environment model.
Go to the next subsection and learn how to draw environment diagrams!