****
******
********
**********
************
**************
****************
******************
********************
NIL
The other important use of LAMBDA expressions is in making closures, functions that capture part of the environment where they're created. You used closures a bit in Chapter 3, but the details of how closures work and what they're used for is really more about how variables work than functions, so I'll save that discussion for the next chapter.
6. Variables
The next basic building block we need to look at are variables. Common Lisp supports two kinds of variables: lexical and dynamic.[67] These two types correspond roughly to 'local' and 'global' variables in other languages. However, the correspondence is only approximate. On one hand, some languages' 'local' variables are in fact much like Common Lisp's dynamic variables.[68] And on the other, some languages' local variables are
To make matters a bit more confusing, many of the forms that deal with variables can be used with both lexical and dynamic variables. So I'll start by discussing a few aspects of Lisp's variables that apply to both kinds and then cover the specific characteristics of lexical and dynamic variables. Then I'll discuss Common Lisp's general-purpose assignment operator, SETF
, which is used to assign new values to variables and just about every other place that can hold a value.
As in other languages, in Common Lisp variables are named places that can hold a value. However, in Common Lisp, variables aren't typed the way they are in languages such as Java or C++. That is, you don't need to declare the type of object that each variable can hold. Instead, a variable can hold values of any type and the values carry type information that can be used to check types at runtime. Thus, Common Lisp is +
function, Common Lisp will signal a type error. On the other hand, Common Lisp
All values in Common Lisp are, conceptually at least, references to objects. [70] Consequently, assigning a variable a new value changes
One way to introduce new variables you've already used is to define function parameters. As you saw in the previous chapter, when you define a function with DEFUN
, the parameter list defines the variables that will hold the function's arguments when it's called. For example, this function defines three variables—x
, y
, and z
—to hold its arguments.
(defun foo (x y z) (+ x y z))
Each time a function is called, Lisp creates new
As with all Common Lisp variables, function parameters hold object references.[71] Thus, you can assign a new value to a function parameter within the body of the function, and it will not affect the bindings created for another call to the same function. But if the object passed to a function is mutable and you change it in the function, the changes will be visible to the caller since both the caller and the callee will be referencing the same object.
Another form that introduces new variables is the LET
special operator. The skeleton of a LET
form looks like this:
(let (
where each NIL
—a plain variable name. The following LET
form, for example, binds the three variables x
, y
, and z
with initial values 10, 20, and NIL
:
(let ((x 10) (y 20) z)
When the LET
form is evaluated, all the initial value forms are first evaluated. Then new bindings are created and initialized to the appropriate initial values before the body forms are executed. Within the body of the LET
, the variable names refer to the newly created bindings. After the LET
, the names refer to whatever, if anything, they referred to before the LET
.
The value of the last expression in the body is returned as the value of the LET
expression. Like function parameters, variables introduced with LET
are rebound each time the LET
is entered.[72]
The LET
variables—the area of the program where the variable name can be used to refer to the variable's binding—is delimited by the form that introduces the variable. This form—the function definition or the LET
—is called the
If you nest binding forms that introduce variables with the same name, then the bindings of the innermost variable x
to hold the function's argument. Then the first LET
creates a new binding with the initial value 2, and the inner LET
creates yet another binding, this one with the initial value 3. The bars on