8

When executing the following I get an error:

(defun caller (func)
  (url-retrieve "http://m0smith.freeshell.org/"
   (lambda (status) (funcall func))))


(caller (lambda() (message "called")))

Result:

error in process filter: Symbol's value as variable is void: func

What is the best way to solve this problem? Basically I need to accept a callback from somewhere else, wrap it in another lambda and use that as the callback to url-retrieve.

If I change caller to

(defun caller (func)
  (url-retrieve "http://m0smith.freeshell.org/"
   `(lambda (status) (funcall ,func))))

it works. However, I cannot do this as func is being passed by flycheck and the macro expansion breaks it. To see the full context of what I am doing: https://gist.github.com/m0smith/b5961fda6afd71e82983

M Smith
  • 279
  • 1
  • 7
  • *What macro expansion?* Your last paragraph is unclear. Post here a full explanation of the problem. Using backquote with comma **is** the solution (one solution). Another could be to use `lexical-let` or setting variable `lexical-binding`. Please make clear what the problem is with the not-shown "macro". – Drew Dec 10 '14 at 15:20
  • I assumed the ` and , were macro expanded. Whatever that form is called then. I would like to work in EMACS 23. Is there a lexical feature available? – M Smith Dec 10 '14 at 15:23
  • If there is no use of `FUNC` beyond the `funcall` then logically you do **not** need lexical binding here. There is nothing wrong with using it, but you do not *need* it, unless some code actually needs to make use of the *variable* `FUNC`. If you do not need it (which is what it looks like, so far), then just replace its occurrence by its value, using backquote with comma. – Drew Dec 10 '14 at 15:23
  • Lexical binding is available in Emacs 23 (and prior), using `lexical-let`. The global variable `lexical-binding` is available in Emacs 24. – Drew Dec 10 '14 at 15:25

2 Answers2

5

You can achieve this locally by using cl.el's lexical-let:

(eval-when-compile '(require 'cl))

(defun my-test-caller (func)
  (lexical-let ((ext-func func))
    (url-retrieve "http://www.google.com"
                  (lambda (status) (funcall ext-func)))))

(my-test-caller #'(lambda() (message "called")))

To be explicit as the help says:

Like `let', but lexically scoped.
The main visible difference is that lambdas inside BODY will create
lexical closures as in Common Lisp.

Now you can get the same effect by enabling lexical-binding which was added in Emacs 24.1. This is a buffer local variable that can be set and will enable lexical closures on all code in the buffer. So your buffer would look like this:

;; -*- lexical-binding: t; -*-

(defun my-lexical-test-caller (func)
  (url-retrieve "http://www.google.com"
                (lambda (status) (funcall func))))

(my-lexical-test-caller
 #'(lambda()
     (message "called from lexical-binding def")))
stsquad
  • 4,626
  • 28
  • 45
  • Thanks. However I get `my-test-caller: Symbol's function definition is void: lexical-let` in my emacs: GNU Emacs 24.4.1 (x86_64-w64-mingw32)` of 2014-10-20 on KAEL – M Smith Dec 10 '14 at 15:24
  • @MSmith - ahh add (require 'cl) – stsquad Dec 10 '14 at 15:27
  • `lexical-let` is defined in `cl-macs.el`. So `(eval-when-compile '(require 'cl))` – Drew Dec 10 '14 at 15:27
  • You don't need to require `cl.el` at runtime, just for this. `lexical-let` is a macro, so it is enough to require it at compile time. – Drew Dec 10 '14 at 15:28
  • 2
    Please don't. Use `lexical-binding`. Flycheck doesn't support Emacs 23 anyway, so there's no point in trying to be compatible with it. –  Dec 10 '14 at 15:51
5

Enable lexical-binding for your library, with M-x add-file-local-variable-prop-line RET lexical-binding RET t.

Please do not use lexical-let as suggested by the other answer. Flycheck itself is not compatible with Emacs 23, so there is no point in trying to maintain Emacs 23 compatibility in your own code.

  • Thanks. That will help so I am not trying to get the older emacs working for no reason – M Smith Dec 10 '14 at 16:22
  • What's wrong with using lexical-let for this? – stsquad Dec 11 '14 at 10:28
  • @stsquad It's slower and more verbose. With `lexical-binding` there is no need for an additional binding, because the argument itself is lexically scoped. Moreover, `lexical-binding` creates true closures, whereas `lexical-let` uses uninterned symbols to emulate lexical binding. –  Dec 11 '14 at 11:38
  • @lunaryorn: isn't there a risk by enabling lexical-binding on a existing buffer of legacy code you may get unforeseen effects? Anyway I've expanded my answer to mention both solutions. – stsquad Dec 11 '14 at 12:00
  • @stsquad Yes, there *could* be unforeseen effects in poorly written legacy code which relies on `let` binding undefined variables dynamically. But again, Flycheck is for Emacs 24 anyway, so we are not talking about legacy code. –  Dec 11 '14 at 12:26