I was experimenting with interplay between Emacs lexical scoping (new feature of Emacs 24) and add-to-list
and found the interplay confusing and I don't know how to make sense of it. Here is a minimal example, except I use set
instead of add-to-list
. (set
is similar to add-to-list
in that it usually takes a quoted variable name)
(eval
'(progn
(setq a "global")
(let ((a "apple"))
(defun my-print-a ()
(print a)
(set 'a "by set")
(print a))
(setq a "mature apple"))
(let ((a "banana"))
(my-print-a))
(print a))
t) ;; t for lexical scoping
Above code prints "mature apple", "mature apple", "by set" in order. The first print result, "mature apple", is as expected for lexical scoping (with support for lexical closures), nothing surprising to see here. But the results of the second and the third print are surprising to me. It's as if (set 'a "by set")
is only recognizing and affecting the global binding of the name a
.
Is this an intended behavior? Or is this a bug? If intended, how does one make sense of this behavior?
Am I right in assuming that it's always the global binding that set
affects as long as lexical scoping is on?
With (eval '(progn ...) nil)
, things work as expected of dynamic scoping and the behavior of (set 'a ...)
is the same as that of (setq a ...)
in that case. Only when one uses lexical scoping and a quoted variable together, this gotcha shows up.
Update:
it seems to be an intended behavior according to the manual. On Void Variables, manual says
Under lexical binding rules, the value cell only holds the variable's global value, i.e. the value outside of any lexical binding construct. When a variable is lexically bound, the local value is determined by the lexical environment; the variable may have a local value if its symbol's value cell is unassigned.
and on Lexical Binding
functions like symbol-value, boundp, and set only retrieve or modify a variable's dynamic binding (i.e. the contents of its symbol's value cell).
symbol-value, boundp, set are functions usually called with quoted variable names ((symbol-value 'var) (boundp 'var) (set 'var 123)
). These functions only get or set a symbol's value cell, and under lexical binding rules, the value cell only holds the global value. So under lexical binding, use of quoted variables only get or set global values (unless the variable is a special variable). Although it still seems odd in that the result is neither lexical (apple) nor dynamic (banana).
The code is not written in the way that Emacs expects a lexical-scoping-enabled program to be written.
http://www.gnu.org/software/emacs/manual/html_node/elisp/Definitions.html
http://www.gnu.org/software/emacs/manual/html_node/elisp/Lexical-Binding.html
http://www.gnu.org/software/emacs/manual/html_node/elisp/Setting-Variables.html
If we add
defvar
that definesa
as a global variable, we can see that all the references toa
in themy-print-a
function turn out to be dynamically bound as the manual explains: