emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Why is Elisp's defvar weird? And is eval_sub broken?


From: Kelly Dean
Subject: Re: Why is Elisp's defvar weird? And is eval_sub broken?
Date: Sat, 14 Feb 2015 07:35:58 +0000

Stefan Monnier wrote:
> The declaration of the var as being dynamically-scoped (aka "special")
> is *local* to the (rest of the) current scope (typically the current file).
>
> This is indispensable so that one package can use a dynamically-bound
> variable `foo' without breaking some other package that expects `foo' to
> be lexically-bound.

desktop.el doesn't use desktop-first-buffer, desktop-buffer-ok-count, and 
desktop-buffer-fail-count as global variables. It only uses them as dynamic 
variables, for implicit arguments and return values of functions. I.e. outside 
of a let-binding, it doesn't use the symbols (except in defvar, just to avoid 
byte compiler warnings).

For that case, what I had in mind was a dynamic-let special form, being the 
same as «let», except with the following case removed:
if (!NILP (lexenv) && SYMBOLP (var)
         && !XSYMBOL (var)->declared_special
         && NILP (Fmemq (var, Vinternal_interpreter_environment)))
  lexenv = Fcons (Fcons (var, tem), lexenv);

so it always just does specbind (var, tem). The byte compiler could flag an 
error if you use dynamic-let on a symbol that's already used as a lexical 
variable. This way, there's no need to use defvar on symbols such as the 
desktop.el symbols above. So defvar can be reserved just for the symbols that 
really are special, i.e. the ones that are used as global variables and for 
which dynamic variables instead of lexical variables are created to shadow the 
globals when you let-bind the symbols using standard «let» (in any file).

A declaration of free dynamic variables for a function could tell the byte 
compiler that those free variables aren't typos, since otherwise the byte 
compiler would expect either the symbol to be declared special or a lexical 
variable by that name to be in scope.

Also have a lexical-let special form (replacing the old lexical-let macro), 
being the same as «let», except with case above replaced by:
CHECK_SYMBOL (var);
if (NILP (lexenv)) lexenv = Qt;
lexenv = Fcons (Fcons (var, tem), lexenv);

and have it never do specbind (var, tem). The byte compiler could give a 
warning if you use lexical-let on a symbol that's declared special. Then 
everybody could use this faster form in the cases where all the variables being 
bound in the «let» are supposed to be lexical. This also has the advantage of 
ensuring that those variables _are_ bound lexically even if you forgot that 
some of the symbols you're using were declared special, and the byte compiler 
can see your intent to use lexicals and warn you about the specials, which 
helps catch bugs.

Then you never need standard (and slow, and bug-prone) «let», except for 
compatibility with old code, and for the rare cases where you might need it for 
some mind-bending macros.

I would vote for ⌜dlet⌝ and ⌜llet⌝ as the names, or at least ⌜dynlet⌝ and 
⌜lexlet⌝, since Elisp code is already too verbose.

For both correctness and speed, have the standard llet (accessible to 
interpreted code) include the CHECK_SYMBOL (var) above, and have the standard 
dlet include a runtime check of shadowing a lexical. Have byte-compiled code 
use a pair of optimized special forms, not accessible to interpreted code, with 
those checks omitted, since they're done at compile time.

> Normally, such conflicts should never happen
> because all special vars should be named with a "package prefix", but
> sadly, reality is different, so it was indispensable to make this
> effect local, to allow lexical-binding code to work reliably.

By using llet, the byte compiler will catch such conflicts, and your code 
(interpreted or compiled) that uses it will work reliably despite the 
conflicts. This means defvar's weird behavior is no longer needed.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]