A closure is a type of Lisp functional object useful for implementing certain advanced access and control structures. Closures give the programmer more explicit control over the environment, by allowing him to "save up" the environment created by the entering of a dynamic contour (i.e. a lambda , do , prog , progv , let , or any of several other special forms), and then use that environment elsewhere, even after the contour has been exited.
(setq a 3)
((lambda (a)
(print (+ a 6)))
10)
(print a)
Initially there is a value cell for a , and the setq form makes
the contents of that value cell be 3 . Then the
lambda -combination is evaluated. a is bound to 10 : the old
value cell, which still contains a 3 , is saved away, and a new
value cell is created with 10 as its contents. The reference to
a inside the lambda expression evaluates to the current binding
of a , which is the contents of its current value cell, namely
10 . So 16 is printed. Then the binding is undone, discarding
the new value cell, and restoring the old value cell which still
contains a 3 . The final print prints out a 3 .
The form (closure var-list function) , where
var-list is a list of variables and function is any function,
creates and returns a closure. When this closure is applied to some
arguments, all of the value cells of the variables on var-list are
saved away, and the value cells that those variables had at the time
closure was called are made to be the value cells of
the symbols. Then function is applied to the argument. (This paragraph
is somewhat complex, but it completely describes the operation of closures;
if you don't understand it, come back and read it again.)
Here is another, lower level explanation. The closure object
stores several things inside of it. First, it saves the function .
Secondly, for each variable in var-list , it remembers what that
variable's value cell was when the closure was created.
Then when the closure is called as a function, it first temporarily restores
the value cells it has remembered, and then applies function to the
same arguments to which the closure itself was applied.
Now, if we evaluate the form
(setq a
((lambda (x)
(closure '(x) (function car)))
3))
what happens is that a new value cell is created for x , and its
contents is a fixnum 3 . Then a closure is created, which remembers
the function car , the symbol x , and that value cell.
Finally the old value cell of x is restored, and the closure is
returned. Notice that the new value cell is still around, because
it is still known about by the closure. When the closure is applied,
this value cell will be restored and the value of x will be 3 .
Because of the way closures are implemented, the variables
to be closed over must not get turned into "local variables" by the compiler.
Therefore, all such variables should be declared special.
In the Lisp Machine's implementation of closures, lambda-binding
never really allocates any storage to create new value cells. Value
cells are only created (sometimes) by the closure function itself.
Thus, implementors of large systems need not worry about storage allocation
overhead from this mechanism if they are not using closures. See the section
on internal formats.
Lisp Machine closures are not closures in the true sense,
as they do not save the whole variable-binding environment; however,
most of that environment is irrelevant, and the explicit
declaration of which variables are to be closed allows the
implementation to have high efficiency.
They also allow the programmer to explicitly choose for each variable
whether it is to be bound at the point of call or
bound at the point of definition (e.g., creation of the closure), a choice
which is not conveniently available in other languages. In addition the
program is clearer because the intended effect of the closure is made
manifest by listing the variables to be affected.
(defun make-list-closure (l)
(closure '(l)
(function (lambda ()
(prog1 (car l)
(setq l (cdr l)))))))
Now we can make as many list generators as we like; they won't get
in each other's way because each has its own value cell for l .
Each of these value cells was created when the make-list-closure
function was entered, and the value cells are remembered by the closures.
(let-closed ((a 5) b (c 'x))
(function (lambda () ...)))
expands into
(local-declare ((special a b c))
(let ((a 5) b (c 'x))
(closure '(a b c)
(function (lambda () ...)))))