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
defvarform also declares the variable as "special", so that it is always dynamically bound even iflexical-bindingis 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)