Emacs: code in defun or defmacro body cannot refer to surrounding lexical variables?

Update to 2013 . Starting with GNU Emacs 24.3.1, (let .. (defun ..)) bytecompiles is just fine, without warning, and the code encoded in the code works the same as the non-compiled code. Just remember to add the lexical-binding: t file variable to the file to be copied. Workarounds at the end of this question are no longer needed.


Lexical Binding - Emacs Lisp Guide contains this paragraph:

Note that functions such as symbol-value, boundp, and set only retrieve or modify the dynamic binding variable (i.e., the contents of the symbol value cell). In addition, code in the body of defun or defmacro cannot reference surrounding lexical variables.

I am not sure if I understood the meaning of the second sentence correctly. In the following code, which should be run in lexical binding mode, the code in the defun body successfully refers to the lexical binding value of the name n .

 (let ((n 0)) (defun my-counter () (incf n))) (my-counter) ;; 1 (my-counter) ;; 2 

This sentence just says that (let .. (defun ..)) is bad practice?


Bypass

 ;; -*- lexical-binding: t -*- ;; a way to define the counter function without byte-compile error or warning (defvar my--counter-func (let ((n 0)) (lambda () (setq n (1+ n))))) (defun my-counter () (funcall my--counter-func)) ;; another way to define the counter function, again without byte-compile error or warning (fset 'my-another-counter (let ((n 0)) (lambda () (setq n (1+ n))))) 

And here is the code to test the above code:

 ;; run: ;; emacs -q --load path-to-the-el-file-of-this-code.el (load "path-to-file-defining-my-counter.elc") ;; loading the ELC file to test if byte-compiled code runs as expected. (print (my-counter)) ;; 1 (print (my-counter)) ;; 2 (print (my-another-counter)) ;; 1 (print (my-another-counter)) ;; 2 
+4
source share
3 answers

Code is not byte-compiled, at least in Emacs 24.1.1. I saved the following code in the file foo.el , which uses setq instead of incf to avoid the possible effects of the cl library:

 ;; -*- lexical-binding: t -*- (let ((n 0)) (defun my-counter () (setq n (1+ n)))) 

When I tried to compile it ( Mx byte-compile-file foo.el ), I received the following warning messages:

 foo.el:3:1:Warning: Function my-counter will ignore its context (n) foo.el:3:1:Warning: Unused lexical variable `n' foo.el:5:11:Warning: reference to free variable `n' foo.el:5:17:Warning: assignment to free variable `n' 

All messages indicate that the code in the defun body cannot refer to the surrounding lexical variable n in accordance with the requirements of the manual.

Actually, when I downloaded the byte-compiled code ( Mx load-file foo.elc ) and changed the form (my-counter) , I got the following erorr:

 Debugger entered--Lisp error: (void-variable n) ... 

Unfortunately, I'm not sure why the code works when evaluated as source code.

+1
source

As I answered gnu.emacs.help, you can use (defalias' foo (lambda ...)) to get around this limitation. And this restriction is removed in the Emacs development code.

+1
source

It is perfectly fine to refer to variables in the lexical region (*) from within defun, just like you do above, and just like the "my-ticker" example on this manual page.

Or I miss the line in the manual that says:

the code in the body of defun or defmacro cannot refer to surrounding lexical variables.

must say something more:

the code in the defun body can only access lexical variables if they are defined within the same lexical scope.

NOTE. Other answers have comments about problems with compiling this type of code. They should be fixed in the latest emacs. I checked in v24.2.50.1 that this byte compiles and loads correctly.

0
source

All Articles