NStal
NStal

Reputation: 999

format of lambda in emacs lisp

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?

Upvotes: 1

Views: 637

Answers (2)

Drew
Drew

Reputation: 30708

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.

Upvotes: 0

Daimrod
Daimrod

Reputation: 5030

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)))))

Upvotes: 3

Related Questions