lambda
and Lexical Scope (Hunk M)================================================================== Hunk M starts here: ==================================================================
lambda
creates a procedure that will execute in the
scope where the lambda
expression was evaluated.
Except for local variables of the procedure itself, including
its arguments, names in the body of the procedure refer to whatever
they refer to at the point where the procedure is created by
lambda
.
This is necessary for preserving lexical scope--the meanings of variable names must be obvious at the point where the procedure is defined.
Local variables created by the procedure have the usual scope rule within the body. (Argument variables are just a special kind of local variable, which get their initial values from the caller.) Other variables are called free variables--that is variables defined outside the procedure, but referred to from inside it.
We say that lambda
creates a closure, a procedure
whose free variable references are "fixed" at the time the procedure
is created. Whenever the procedure references a free variable, it
will will refer to the bindings of those variables in the environment
where the procedure was created.
Consider the following small program
(define foo 1) (define (baz) foo) (define (quux) (let ((foo 6)) (baz))) (quux)
When quux
is called, it will bind its local variable foo
and then call baz
. When baz
is called from quux
,
however, it will still see the top level binding of foo
, whose
value is 1
. The result of the call to baz
will be
1
, and that value will be returned as the value of the call to
quux
as well.
There is a very good reason for this, and it's the rule used by most
programming languages. It is important that the meaning of a
procedure be fixed where it is defined, rather than having the
meaning depend on where it is called from. You want to be able to
look at the code, and see that the name foo
refers to particular
variable, namely the one that's visible there, at the top level.
You don't want to have to worry about the meaning of the procedure
baz
changing, depending on where it's called from.
A block structure diagram may make this clearer. We'll just show the
part for the procedure baz
:
(define (quux) (let ((foo 6)) +---------------------------+ | (baz) scope of foo | )) +---------------------------+
This emphasizes the fact that the local foo
really is local.
The definition of baz
is not inside the box, so it can't
ever see foo
's local variable foo
. (The fact that
baz
is called from inside the box doesn't matter.)
Conceptually, the procedure baz
returns to the environment
where it was created before it executes, and even before it binds its
arguments.
In early Lisps, a different rule was used, called dynamic scope.
In those Lisps, the call to baz
would see the most
recent binding of foo
. In this case, it would see the
binding created by quux
just before the call to foo
.
This led to very inscrutable bugs, because a procedure would work
sometimes, and not others, depending on the names of variables
bound in other procedures.
(Dynamic scoping is generally considered to have been a big mistake, and was fixed in recent versions of Lisp, such as Common Lisp, which were influenced by Scheme.)