From: Kelly Dean <kelly@prtime.org>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: emacs-devel@gnu.org
Subject: Re: Why is Elisp's defvar weird? And is eval_sub broken?
Date: Sat, 14 Feb 2015 07:35:58 +0000 [thread overview]
Message-ID: <miynlVlA0Vcsh6YTYHk4wEPMQ2qNsuPM4rVNaILGIEG@local> (raw)
In-Reply-To: <jwvmw4h7ie6.fsf-monnier+emacs@gnu.org>
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.
next prev parent reply other threads:[~2015-02-14 7:35 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-02-12 21:32 Why is Elisp's defvar weird? And is eval_sub broken? Kelly Dean
2015-02-13 19:03 ` Stefan Monnier
2015-02-14 7:35 ` Kelly Dean [this message]
2015-02-14 14:36 ` Stefan Monnier
2015-02-15 14:17 ` Daniel Colascione
2015-02-16 5:42 ` Kelly Dean
2015-02-16 7:40 ` Stefan Monnier
2015-02-17 23:39 ` Kelly Dean
2015-02-18 22:29 ` Stefan Monnier
2015-02-19 10:32 ` Kelly Dean
2015-02-19 13:23 ` Stefan Monnier
2015-02-20 0:11 ` Kelly Dean
2015-02-20 2:02 ` Stefan Monnier
2015-02-22 4:11 ` Proposal for a closed-buffer tracker Kelly Dean
2015-02-22 15:53 ` Eli Zaretskii
2015-02-22 22:03 ` Stefan Monnier
2015-02-22 22:23 ` Dmitry Gutov
2015-02-23 13:53 ` Artur Malabarba
2015-02-23 16:44 ` Eli Zaretskii
2015-02-22 21:59 ` Stefan Monnier
2015-02-28 10:15 ` Artur Malabarba
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=miynlVlA0Vcsh6YTYHk4wEPMQ2qNsuPM4rVNaILGIEG@local \
--to=kelly@prtime.org \
--cc=emacs-devel@gnu.org \
--cc=monnier@iro.umontreal.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.