Things are getting crazy now! We have environments popping up, getting
passed around, and being modified. We have functions defined in Scheem
that are recursively calling
evalScheem. How does it all fit together?
For any given variable reference in a Scheem program, how do we know
which environment it will use? How do we know we created an interpreter
that passes the right environment to our
make-account in Scheme
make-account takes a beginning bank balance and returns
a function that updates the bank balance. In the code, the variable
a is defined to be a new bank balance of $100. The user then
immediately spends $20 by calling
a, and gets back the new lower balance.
The colored boxes indicate the scope of all the variables. The outermost
scope is the global environment. We add
a to this
global environment. The yellow box indicates the scope of
The purple box indicates the scope of
The scoping rule
The rule is that expressions
can refer to any variable associated with any box they are inside of.
For example, in the purple box the expression
(+ balance amt)
can refer to
amt since it is inside the purple box, and to
because it is also inside the yellow box. In the expression
it is not possible to refer to
amt because the expression
is not inside the purple box.
If a language has lexical scope then it is always possible to draw colored boxes like this for any expression. No matter what happens when you run the program you can always figure out which binding will be used for every reference to every variable.
makeAccount function is returning a closure that updates
each time it is called. If you haven't seen this before,
take a look at Closure lesson 8: Nested Functions
and Closure lesson 9: Stateful Closures.
It's mind-blowing stuff.
Connection with our Scheem interpreter
So how does lexical scope connect to our Scheem interpreter? When
we defined how
lambda-one worked we said that it evaluates to a function
value that takes a single argument
arg and binds the variable name
to that value, then evaluates the body of the
lambda-one in that
The critical thing here is that the environment that the function
body will be evaluated in is being created and specified when the
function is being created, in the evaluation of the
That means when a function is created we know exactly how the bindings
will work out for when we evaluate the function. It doesn't matter
what happens at runtime, when it comes time to evaluate the function
we know that the environment made when the function was created will be used.
That's lexical scope.