Emacs Lisp: Closures Exposed
Jun 14, 2013
Jun 14, 2013
I recently learned about some interesting wrinkles in Emacs Lisp that make it useful for learning about closures. First let’s talk about closures in more reasonable languages.
What do the following code snippets do?
Python:
def fun1():
x = 12
return lambda y: x*y
fun1()(3)
JavaScript:
var fun1 = function() {
var x = 12;
return function (y) {
return x*y;
};
};
fun1()(3)
The answer is, they print 36. Each returns outer function returns an
anonymous function that is then called with the value 3
a an
input. But what about the equivalent code in Emacs Lisp?:
(funcall (let ((x 12))
(lambda (y)
(* x y))) 3)
This throws an error: (void-variable x)
. Huh? What is going on? The
answer is that most modern languages, including Python and JavaScript,
are lexically scoped, whereas Emacs Lisp, being many decades old, is
dynamically scoped. What does this mean? It has to do with the
resolution of free variables. In each code example above, x
is a
free variable in the innermost function call because it was not passed
as an argument and has no local definition. How is the value of a
free variable resolved then? In lexically scoped languages, the
language looks for definitions of free variables in the immediate
lexical environment, whereas in dynamically scoped languages, the
language looks for definitions of free variables in the immediate
evaluating environment.
This explains our differing results above. Python and JavaScript see a
free variable x
in the anonymous function returned by fun1
. They
go look for a definition in the enclosing lexical scope (the
definition of fun1
), see that here x is defined to be 12, and so use
12 as the value for x. Emacs Lisp, on other hand, does something
different. Using funcall
, we asked it to call the following with
argument 3
:
(let ((x 12))
(lambda (y)
(* x y)))
The let
statement says that during the evaluation of the following
code, let the value of x
be 12. The keyword here is
evaluation. Emacs Lisp allows x
to be 12 during the evaluation,
which returns a function of y
. The evaluation is then over, so Emacs
Lisp promptly forgets the value of x
! The lambda function returned
has no notion of the fact that x
is twelve in its lexical
environment; when this function is called by funcall
it is called in
the global scope, in which there is no definition of x
, hence
(void-variable x)
.
This seems weird by modern lights, because most languages we write
code in regularly are lexically scoped. Emacs Lisp gets ever weirder
though. Remember when I said that python and JavaScript “go look for a
definition in the enclosing lexical scope”? That was sort of a
lie. They don’t really go looking through the text of your code trying
to find definitions, what actually happens is that when fun1
evaluates, it doesn’t just return a bare anonymous function. Instead,
the function is returned along with some information about its lexical
environment. This information is called a closure, and usually
consists of the values that any free variables had at the time the
function was defined. When python or JavaScript needs to know the
value of x
during evaluation, they go look up its definition in the
closure. You can’t actually examine closures in the language itself;
they’re specially protected and hidden away in the implementation. But
let’s go back to Emacs Lisp for a moment. When we execute
(let ((x 12)) (lambda (y) (* x y)))
We get back:
(lambda (y) (* x y))
which is a function definition, pretty much what we expected. Notice
that this expression evaluated on its own (which is effectively what we
were doing when we did the funcall
above) has no way of figuring out
what x
is (i.e. no closure containing a definition of x
). Where
Emacs Lisp gets weird is that it is possible to have lexical scoping,
but its an optional feature that you have to turn on. Let’s try it:
(setq lexical-binding t)
(let ((x 12))
(lambda (y)
(* x y)))
Now the second expression evaluates to:
(closure ((x . 12) t) (y) (* x y))
Aha! A closure! The first part of the closure is a list of pairs that
describe the lexical environment. We can see that x
is bound
to 12. The remainder of the closure is the argument list and function
body. Because lexical scope was tacked onto Elisp decades after its
creation, it doesn’t hide closures away from you the way more modern
languages do; they are right there, available to be inspected as data
structures in the language itself, which is really pretty bizarre. If
we funcall this with 3 as an argument, we get 36, just like we did in
python or JavaScript. Internally, these languages are doing the same
thing, we as programmers are just not allowed to see it. Emacs Lisp is
adorable trusting; however, which means we can do stupidly silly
things. The closure is just a list, so we can use it as we would any
other:
(setq c (let ((x 12))
(lambda (y)
(* x y))))
The closure is now bound to the symbol c
, and we can funcall it:
(funcall c 3)
;; => 36
We can also modify it, e.g.:
(setcdr (caadr c) 5)
We set the cdr
of the car
of the car
of the cdr
of c
to 5. This changes c
to:
(closure ((x . 5) t) (y) (* x y))
And now:
(funcall c 3)
;; => 15
I personally find this totally hilarious, and I hope its at least somewhat illuminating with respect to what a closure is as well.
Addendum (May 15th, 2014): As Nic Ferrier pointed out on twitter, this all only works interactively and not when byte-compiling. The exposure of closures illustrated above is considered an implementation detail by the Emacs developers.