Lisp often "creates a binding" for a variable by allocating a piece of storage to hold the variable's value and putting the value into the newly allocated memory. Binding is a very general mechanism for implementing lexical scope for variables, but it has other uses depending upon the lifetime of the binding. We'll revisit this in Chapter 8 when we study lifetime and visibility.
Yes, Lisp allocates storage for new bindings. While this sounds like it could be horribly inefficient, we've said nothing yet about where Lisp allocated the storage. For example, Lisp binds function parameters to actual values, but allocates the storage on the stack just like any other programming language. Lisp creates bindings in the heap if it can't determine that the binding has a lifetime which ends when the binding form finishes executing.
Lisp gives each binding a name. Otherwise, how would your program refer to the binding? Simple, eh? Hold on...
It is quite common for multiple bindings to share the same name. For example:
(let ((a 1)) (let ((a 2)) (let ((a 3)) ...)))
Here, A
has three distinct bindings by the time the body
(marked by ...
) executes in the innermost LET
.
This is not to say that the above example is representative of typical Lisp code, however.
;; Here, A has no binding. (let ((a 1)) ;; Here, the innermost binding of A has the value 1. (let ((a 2)) ;; Here, the innermost binding of A has the value 2. (let ((a 3)) ;; Here, the innermost binding of A has the value 3. ...)))
As you can see, the notion of innermost binding depends on the relative position of your program's code to the form that established a particular binding. If you look at how binding forms are nested (easy to do if you indent your code as shown above) then the program has access to bindings created around, or enclosing, your program code.
One more thing you should know is that an outer binding is still visible through inner binding forms, as long as the inner binding form does not bind the same symbol:
;; Here, A and B have no binding. (let ((a 1) (b 9)) ;; Here, the innermost binding of A has the value 1, ;; and the binding of B has the value 9. (let ((a 2)) ;; Here, the innermost binding of A has the value 2. ;; The binding of B still has the value 9. (let ((a 3)) ;; Here, the innermost binding of A has the value 3. ;; B still has the value 9 from the outermost LET form. ...)))
When a binding form binds a new value to an existing symbol, the previous value becomes shadowed. The value of the outer binding is hidden (but not forgotten) while your program code executes inside the inner binding form. But as soon as your program leaves the inner binding form, the value of the outer binding is restored. For example:
(let ((z 1)) ;; Here, the innermost binding of Z has the value 1. (let ((z 2)) ;; Here, the innermost binding of Z has the value 2. ...) ;; Now we're outside the inner binding form, ;; and we again see the binding with the value 1. ...)
The SETQ
form changes the value of an existing binding:
(let ((z 1)) ;; Here, the innermost binding of Z has the value 1. (setq z 9) ;; Now the value of Z is 9. (let ((z 2)) ;; Here, the innermost binding of Z has the value 2. ...) ;; Now we're outside the inner binding form, ;; and we again see the outer binding of Z with the value 9. ...)
The SETQ
form above changed the value of the outer binding
of Z
for the remainder of the outer LET
form.
This is often the wrong thing to do. The problem is that you now have to
look in two places to discover the value of Z
-- first at the
binding forms, then in the program code for assignments such as
SETQ
. While the binding forms are indented by convention
(many Lisp editors do this as you type), the assignment form, as part of
the body code of the program, gets no special indentation; this makes it
harder to spot when you read the program.
We can quite easily avoid the assignment in the previous example by introducing a new binding:
(let ((z 1)) ;; Here, the innermost binding of Z has the value 1. (let ((z 9)) ;; Now the value of Z is 9. (let ((z 2)) ;; Here, the innermost binding of Z has the value 2. ...) ;; Now we're outside the innermost binding form, ;; and we again see the middle binding of Z with the value 9. ...) ;; Here, we see the outermost binding of Z with the value 1. ...)
Now all of the bindings of Z
are apparent from the
relative indentation of the LET
forms. While reading the
program, all we have to do to find the right binding for Z
at
any point in our program code (the ...
in the example) is to scan
vertically looking for a LET
form at an outer level of
indentation.
When a SETQ
form refers to a variable that is not bound by
an enclosing LET
form, it assigns a value to the global or
special value of the symbol. A global value is accessible anywhere it's
not shadowed, and stays available for as long as the Lisp system runs.
We'll look at special variables in Chapter 8.
(setq a 987) ;; Here, A has the global value 987. (let ((a 1)) ;; Here, the binding of A to the value 1 shadows the global value. ...) ;; Now the global value of A is again visible. ...