Dynamic Bindings
Variable Bindings
Function Bindings
The CL package defines the following macro which
more closely follows the Common Lisp let
form:
let
except that the bindings it
establishes are purely lexical. Lexical bindings are similar to
local variables in a language like C: Only the code physically
within the body of the lexical-let
(after macro expansion)
may refer to the bound variables.
(setq a 5) (defun foo (b) (+ a b)) (let ((a 2)) (foo a)) => 4 (lexical-let ((a 2)) (foo a)) => 7
In this example, a regular let
binding of a
actually
makes a temporary change to the global variable a
, so foo
is able to see the binding of a
to 2. But lexical-let
actually creates a distinct local variable a
for use within its
body, without any effect on the global variable of the same name.
The most important use of lexical bindings is to create closures. A closure is a function object that refers to an outside lexical variable. For example:
(defun make-adder (n) (lexical-let ((n n)) (function (lambda (m) (+ n m))))) (setq add17 (make-adder 17)) (funcall add17 4) => 21
The call (make-adder 17)
returns a function object which adds
17 to its argument. If let
had been used instead of
lexical-let
, the function object would have referred to the
global n
, which would have been bound to 17 only during the
call to make-adder
itself.
(defun make-counter () (lexical-let ((n 0)) (function* (lambda (&optional (m 1)) (incf n m))))) (setq count-1 (make-counter)) (funcall count-1 3) => 3 (funcall count-1 14) => 17 (setq count-2 (make-counter)) (funcall count-2 5) => 5 (funcall count-1 2) => 19 (funcall count-2) => 6
Here we see that each call to make-counter
creates a distinct
local variable n
, which serves as a private counter for the
function object that is returned.
Closed-over lexical variables persist until the last reference to
them goes away, just like all other Lisp objects. For example,
count-2
refers to a function object which refers to an
instance of the variable n
; this is the only reference
to that variable, so after (setq count-2 nil)
the garbage
collector would be able to delete this instance of n
.
Of course, if a lexical-let
does not actually create any
closures, then the lexical variables are free as soon as the
lexical-let
returns.
Many closures are used only during the extent of the bindings they
refer to; these are known as ``downward funargs'' in Lisp parlance.
When a closure is used in this way, regular Emacs Lisp dynamic
bindings suffice and will be more efficient than lexical-let
closures:
(defun add-to-list (x list) (mapcar (function (lambda (y) (+ x y))) list)) (add-to-list 7 '(1 2 5)) => (8 9 12)
Since this lambda is only used while x
is still bound,
it is not necessary to make a true closure out of it.
You can use defun
or flet
inside a lexical-let
to create a named closure. If several closures are created in the
body of a single lexical-let
, they all close over the same
instance of the lexical variable.
The lexical-let
form is an extension to Common Lisp. In
true Common Lisp, all bindings are lexical unless declared otherwise.
lexical-let
, except that the bindings
are made sequentially in the manner of let*
.