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*.