Go to the first, previous, next, last section, table of contents.

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. I'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.)


Go to the first, previous, next, last section, table of contents.