1

I'm tring to apply closure in emacs lisp.And I find a post here: How do I do closures in Emacs Lisp?

with some code like:

(defun foo (x) `(lambda () ,x)) (message (string (funcall (foo 66))))

But following the emacs documentation lambda should be formated like '(lambda () x) ==> using this format ,I got an ERROR :Symbol's value as variable is void: x

When " , " is add betwenn "()" and "x" ,everything goes right .

Why?

Drew
  • 29,895
  • 7
  • 74
  • 104
NStal
  • 959
  • 1
  • 8
  • 19

2 Answers2

3

This happens because Emacs Lisp is dynamically scoped thus foo returns a lambda where x is free. That's what the error tells you.

To do a closure in Emacs Lisp you have to use lexical-let which simulates a lexical binding and therefore allows you to make a real closure.

(defun foo (x)
  (lexical-let ((x x))
               (lambda () x)))

(message (string (funcall (foo 66))))

Here are some links from the Emacs Wiki:

  1. Dynamic Binding Vs Lexical Binding
  2. Fake Closures

Note that you could have defined x with a let like this:

(defun foo (x)
  (lambda () x))

(message (string (let ((x 66)) (funcall 
                                (foo 'i-dont-care)))))
Daimrod
  • 4,902
  • 23
  • 25
  • Actually, taht code is even hackier, it is equivalent to: `(defun foo (x) (list 'lambda nil x))` so `(foo 66)` returns `(lambda () 66)` and I am sure there'#s all sorts of inadvertent capturing possible here... – Vatine Dec 23 '11 at 13:10
  • @Vatine: Yes I know what does the backquote, but you have to understand what does the quote => it prevents the evaluation of the form, so `(defun foo (x) '(lambda () x))`, `(foo 66)` returns `(lambda () x)` and then you try to funcall this, but the interpreter doesn't know what it is. Contrary to the backquote which allows the evaluation with the comma. – Daimrod Dec 23 '11 at 13:40
  • But in the original post there is a backquote, so it returns `(lambda () 66)`, not `(lambda () x)`. – Vatine Dec 23 '11 at 15:18
  • @Vatine: Yes, and with a backquote it works. If the documentation says that lambda should be formated with a quote it's for higher-order like this `(mapcar '(lambda (el) (+ el 1))` '(1 2 3 4))`, but in your case you want to evaluate `x` to build your closure. – Daimrod Dec 23 '11 at 15:38
0

This answer gives a bit of detail behind the first part of @Daimrod's correct answer.

Your question is why this works:

(defun foo (x) `(lambda () ,x)) (message (string (funcall (foo 66))))

and this doesn't work:

(defun foo (x) '(lambda () x)) (message (string (funcall (foo 66))))

First, the quote (') is unnecessary in the second, because in Emacs Lisp lambda forms are self-evaluating. That is, '(lambda (...) ...) generally acts the same as (lambda (...) ...).

But what does the backquote (`), instead of quote ('), do?

Inside a backquoted expression, a comma (,) means replace the next expression by its value, that is, evaluate it. So this:

`(lambda () ,x)

means create and return a list whose first element is the symbol lambda (that's not evaluated), whose second element is () (that's not evaluated), and whose third element is the value of variable x. It's equivalent to evaluating this code, which uses function list:

(list 'lambda '() x)

That's what you want: replace x by its current value (in this case, its value inside the function foo, that is, the value of foo's argument).

But this:

'(lambda () x)

(and likewise, because a lambda form is self-evaluating, (lambda () x)) returns this list: (lambda () x). And when that's evaluated using dynamic scoping (the default scoping regime in Emacs Lisp), x is unbound - it has no value. So a void-variable error is raised.

Drew
  • 29,895
  • 7
  • 74
  • 104