How do I do closures in Emacs Lisp?

2019-01-08 11:38发布

I'm trying to create a function on the fly that would return one constant value.

In JavaScript and other modern imperative languages I would use closures:

function id(a) {
    return function() {return a;};
}

but Emacs lisp doesn't support those.

I can create mix of identity function and partial function application but it's not supported either.

So how do I do that?

7条回答
祖国的老花朵
2楼-- · 2019-01-08 12:06

Emacs lisp only has dynamic scoping. There's a lexical-let macro that approximates lexical scoping through a rather terrible hack.

查看更多
何必那么认真
3楼-- · 2019-01-08 12:07

Stupid idea: how about:

(defun foo (x)
  `(lambda () ,x))

(funcall (foo 10))  ;; => 10
查看更多
够拽才男人
4楼-- · 2019-01-08 12:12

Found another solution with lexical-let

(defun foo (n) 
    (lexical-let ((n n)) #'(lambda() n)))

(funcall (foo 10)) ;; => 10
查看更多
家丑人穷心不美
5楼-- · 2019-01-08 12:16

I am not firm in Emacs Lisp, but as far as I know, a big difference from Common Lisp is that it uses dynamic scoping throughout. The Emacs Lisp Manual states that Emacs Lisp doesn't have closures.

I'll try to apply my theoretical knowledge of dynamic scoping.

If you have a function id which just returns the value of my-id:

(defun id ()
  my-id)

and you use it in some other function:

(defun some-other-place ()
  (id))

and somewhere on the way to the call of id you bind my-id through e.g. a let:

(defun even-elsewhere ()
  (let ((my-id 5))
    (some-other-place)))

this should give back 5.

I know that dynamic scoping is a strange beast when you are used to lexical scoping, but perhaps you can use this to implement your desired behaviour.

查看更多
我只想做你的唯一
6楼-- · 2019-01-08 12:18

Real (Not Fake) Closures in Emacs 24.

Although Emacs 24 has lexical scooping when the variable lexical-binding has value t, the defun special form doesn’t work properly in lexically bound contexts (at least not in Emacs 24.2.1.) This makes it difficult, but not impossible, to define real (not fake) closures. For example:

(let ((counter 0))
   (defun counting ()
    (setq counter (1+ counter))))

will not work as expected because the symbol counter in the defun will be bound to the global variable of that name, if there is one, and not the lexical variable define in the let. When the function counting is called, if the global variable doesn’t, exist then it will obviously fail. Hoever if there is such a global variable it be updated, which is probably not what was intended and could be a hard to trace bug since the function might appear to be working properly.

The byte compiler does give a warning if you use defun in this way and presumably the issue will be addressed in some future version of Emacs, but until then the following macro can be used:

(defmacro defun** (name args &rest body)
  "Define NAME as a function in a lexically bound context.

Like normal `defun', except that it works correctly in lexically
bound contexts.

\(fn NAME ARGLIST [DOCSTRING] BODY...)"
  (let ((bound-as-var (boundp  `,name)))
    (when (fboundp `,name)
      (message "Redefining function/macro: %s" `,name))
    (append
     `(progn
        (defvar ,name nil)
        (fset (quote ,name) (lambda (,@args) ,@body)))
     (if bound-as-var
         'nil
         `((makunbound `,name))))))

If you define counting as follows:

(let ((counter 0))
  (defun** counting ()
    (setq counter (1+ counter))))

it will work as expected and update the lexically bound variable count every time it is invoked, while returning the new value.

CAVEAT: The macro will not work properly if you try to defun** a function with the same name as one of the lexically bound variables. I.e if you do something like:

(let ((dont-do-this 10))
  (defun** dont-do-this ()
    .........
    .........))

I can’t imagine anyone actually doing that but it was worth a mention.

Note: I have named the macro defun** so that it doesn’t clash with the macro defun* in the cl package, however it doesn’t depend in any way on that package.

查看更多
登录 后发表回答