Page:AIM-453.djvu/49

From Wikisource
Jump to navigation Jump to search
There was a problem when proofreading this page.
Steele and Sussman
47
The Art of the Interpreter

This is more general than PRINT-10 in that it allows us to wrap a binding of RADIX around any piece of code, not just a call to PRINT. (In a more realistic example, we might package up the bindings of a dozen parameters in a similar manner.)

There are two possibilities: should the argument to RADIX-10 be a closed procedure or an open LAMBDA-expression? If closed:

(DEFINE (DO-SOMETHING-INTERESTING X FUN)
        (RADIX-10 (LAMBDA () (FORMAT-HAIR 'FOO (CADR X) FUN))))

(FORMAT-HAIR takes several arguments, one of them a procedure and presumably calls PRINT at some level), then the binding of RADIX in RADIX-10 will not be apparent to PRINT, because the environment of the call to FORMAT-HAIR is that of the closed procedure, which in turn is that of the call to RADIX-10 within DO-SOMETHING-INTERESTING. Thus it fails to work at all. If the argument to RADIX-10 is left open:

(DEFINE (DO-SOMETHING-INTERESTING X FUN)
        (RADIX-10 '(LAMBDA () (FORMAT-HAIR 'FOO (CADR X) FUN))))

then this fails to work at all because of a variable naming conflict with FUN. The third argument passed to FORMAT-HAIR will evaluate to the argument which was passed to RADIX-10, namely the quoted lambda expression. This is similar to the MAPCAR bug that originally got us thinking about lexical scoping in Part One.

A solution to this problem is to maintain separate environments for lexical and dynamic variables; this will guarantee that the two kinds cannot interfere with each other. This will require a special syntax for distinguishing references to and bindings of the two kinds of variables. We will choose to encode lexical variables as atomic symbols, as before, and dynamic variables as lists of the form (DYNAMIC X), where X is the name of the dynamic variable. (This choice is completely arbitrary. We could have chosen to encode the two kinds as (LEXICAL X) and X; or as (LEXICAL X) and (DYNAMIC X), leaving atomic symbols as such free to encode yet something else; but we have chosen this because in practice most variable references, even in a purely dynamically scoped LISP, are lexical, or can be considered so.)

In our new interpreter (see Figure 15) we call the two environments ENV (lexical) and DENV (dynamic). The syntax of LAMBDA-expressions is extended to accommodate two kinds of bindings; for example,

(LAMBDA (X Y (DYNAMIC Z) W) ...)

takes four arguments, and binds the parameters X, Y, and W lexically, and Z dynamically. Using this syntax, we could write RADIX-10 in this way:

(DEFINE (RADIX-10 FUN)
        ((LAMBDA ((DYNAMIC RADIX (FUN))
         10.))