Suppose I have a file named elisp-defvar-test.el
containing:
;;; elisp-defvar-test.el --- -*- lexical-binding: t -*-
(defvar my-dynamic-var)
(defun f1 (x)
"Should return X."
(let ((my-dynamic-var x))
(f2)))
(defun f2 ()
"Returns the current value of `my-dynamic-var'."
my-dynamic-var)
(provide 'elisp-dynamic-test)
;;; elisp-defvar-test.el ends here
I load this file and then go into the scratch buffer and run:
(setq lexical-binding t)
(f1 5)
(let ((my-dynamic-var 5))
(f2))
(f1 5)
returns 5 as expected, indicating that the body of f1
is treating my-dynamic-var
as a dynamically scoped variable, as expected. However, the last form gives a void-variable error for my-dynamic-var
, indicating that it is using lexical scoping for this variable. This seems at odds with the documentation for defvar
, which says:
The
defvar
form also declares the variable as "special", so that it is always dynamically bound even iflexical-binding
is t.
If I change the defvar
form in the test file to supply an initial value, then the variable is always treated as dynamic, like the documentation says. Can anyone explain why the scoping of a variable is determined by whether or not defvar
was supplied with an initial value when declaring that variable?
Here is the error backtrace, in case it matters:
Debugger entered--Lisp error: (void-variable my-dynamic-var)
f2()
(let ((my-dynamic-var 5)) (f2))
(progn (let ((my-dynamic-var 5)) (f2)))
eval((progn (let ((my-dynamic-var 5)) (f2))) t)
elisp--eval-last-sexp(t)
eval-last-sexp(t)
eval-print-last-sexp(nil)
funcall-interactively(eval-print-last-sexp nil)
call-interactively(eval-print-last-sexp nil nil)
command-execute(eval-print-last-sexp)