Quoth the docstring of special-variable-p
:
special-variable-p is a built-in function in `src/eval.c'.
(special-variable-p SYMBOL)
Probably introduced at or before Emacs version 24.1.
Return non-nil if SYMBOL's global binding has been declared special.
A special variable is one that will be bound dynamically, even in a
context where binding is lexical by default.
Note the subtlety in the last sentence, about how a special variable would be treated in a lexical context (such as your lexical-binding
buffer).
Quoth the manual entry for special-variable-p
:
-- Function: special-variable-p symbol
This function returns non-‘nil’ if SYMBOL is a special variable
(i.e., it has a ‘defvar’, ‘defcustom’, or ‘defconst’ variable
definition). Otherwise, the return value is ‘nil’.
Note that since this is a function, it can only return non-‘nil’
for variables which are permanently special, but not for those that
are only special in the current lexical scope.
This tells us that only variables which have been declared as special count as special. So, while setq
will change the global value of x
so long as it is a free variable, special-variable-p
will return non-nil
only after x
is declared special.
Modifying your example a bit may help to highlight why this distinction is important:
;; -*- lexical-binding: t -*-
(defun setq-x ()
(setq x 10))
(defvar y 20)
(setq-x)
(let ((x 20))
(setq-x)
(message "values x: %s, y: %s\nspecial x: %s, y: %s"
x y
(special-variable-p 'x)
(special-variable-p 'y)))
When byte-compiled, this gives:
values x: 20, y: 20
special x: nil, y: t
If the first setq-x
made x
special, then x
would have to be 10 after the second call to setq-x
. Instead, x
is let
-bound like a local lexical variable that's in a different scope to that of setq-x
.
The conclusion is that it's fine for users to set undeclared variables (usually user options defined in packages that are yet to be loaded). But Elisp libraries ought to declare their variables or those from other libraries appropriately.
What type of variable binding or variable or symbol is setq
creating, when the variable hasn't been declared prior setq
?
setq
considers variables without a local lexical value as effectively being dynamic. So in your example, (setq x 10)
is the same as (set 'x 10)
(which wouldn't be the case if x
were a lexical variable). You can see this towards the end of the definition of setq
:
DEFUN ("setq", Fsetq, Ssetq, 0, UNEVALLED, 0,
doc: /* Set each SYM to the value of its VAL.
The symbols SYM are variables; they are literal (not evaluated).
The values VAL are expressions; they are evaluated.
Thus, (setq x (1+ y)) sets `x' to the value of `(1+ y)'.
The second VAL is not computed until after the first SYM is set, and so on;
each VAL can use the new value of variables set earlier in the `setq'.
The return value of the `setq' form is the value of the last VAL.
usage: (setq [SYM VAL]...) */)
(Lisp_Object args)
{
Lisp_Object val = args, tail = args;
for (EMACS_INT nargs = 0; CONSP (tail); nargs += 2)
{
Lisp_Object sym = XCAR (tail);
tail = XCDR (tail);
if (!CONSP (tail))
xsignal2 (Qwrong_number_of_arguments, Qsetq, make_fixnum (nargs + 1));
Lisp_Object arg = XCAR (tail);
tail = XCDR (tail);
val = eval_sub (arg);
/* Like for eval_sub, we do not check declared_special here since
it's been done when let-binding. */
Lisp_Object lex_binding
= ((!NILP (Vinternal_interpreter_environment) /* Mere optimization! */
&& SYMBOLP (sym))
? Fassq (sym, Vinternal_interpreter_environment)
: Qnil);
if (!NILP (lex_binding))
XSETCDR (lex_binding, val); /* SYM is lexically bound. */
else
Fset (sym, val); /* SYM is dynamically bound. */
}
return val;
}
lexical-binding
), except for how specialness (aka dynamicness) is declared.