From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Kelly Dean Newsgroups: gmane.emacs.devel Subject: Why is Elisp's defvar weird? And is eval_sub broken? Date: Thu, 12 Feb 2015 21:32:01 +0000 Message-ID: NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Trace: ger.gmane.org 1423776834 9635 80.91.229.3 (12 Feb 2015 21:33:54 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Thu, 12 Feb 2015 21:33:54 +0000 (UTC) To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Thu Feb 12 22:33:44 2015 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1YM1OJ-0001zS-TZ for ged-emacs-devel@m.gmane.org; Thu, 12 Feb 2015 22:33:44 +0100 Original-Received: from localhost ([::1]:52234 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YM1OJ-0005CQ-BM for ged-emacs-devel@m.gmane.org; Thu, 12 Feb 2015 16:33:43 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:52629) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YM1O5-0005C7-Gq for emacs-devel@gnu.org; Thu, 12 Feb 2015 16:33:30 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1YM1O1-0008Ig-Gw for emacs-devel@gnu.org; Thu, 12 Feb 2015 16:33:29 -0500 Original-Received: from relay3-d.mail.gandi.net ([2001:4b98:c:538::195]:35154) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1YM1O1-0008Db-CG for emacs-devel@gnu.org; Thu, 12 Feb 2015 16:33:25 -0500 Original-Received: from mfilter15-d.gandi.net (mfilter15-d.gandi.net [217.70.178.143]) by relay3-d.mail.gandi.net (Postfix) with ESMTP id DC5DEA80B8 for ; Thu, 12 Feb 2015 22:33:20 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at mfilter15-d.gandi.net Original-Received: from relay3-d.mail.gandi.net ([217.70.183.195]) by mfilter15-d.gandi.net (mfilter15-d.gandi.net [10.0.15.180]) (amavisd-new, port 10024) with ESMTP id H0SWjOtmwR5T for ; Thu, 12 Feb 2015 22:33:19 +0100 (CET) X-Originating-IP: 66.220.3.179 Original-Received: from localhost (gm179.geneticmail.com [66.220.3.179]) (Authenticated sender: kelly@prtime.org) by relay3-d.mail.gandi.net (Postfix) with ESMTPSA id 4F6E8A80C0 for ; Thu, 12 Feb 2015 22:33:15 +0100 (CET) X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2001:4b98:c:538::195 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:182981 Archived-At: desktop.el has these top-level definitions: (defvar desktop-first-buffer) (defvar desktop-buffer-ok-count) (defvar desktop-buffer-fail-count) The docstring for defvar says: =E2=8C=9CDefine SYMBOL as a variable, and return SYMBOL. ... The `defvar' form also declares the variable as "special", so that it is always dynamically bound even if `lexical-binding' is t. ... If INITVALUE is missing, SYMBOL's value is not set. If SYMBOL has a local binding, then this form affects the local binding.=E2=8C=9D But that's wrong. If INITVALUE is missing, and lexical-binding is t (as i= s the case in desktop.el), then not only is the value not set, but also t= he variable is _not_ declared special, even if the defvar is at top level= . That means that even after loading desktop.el, if you let-bind the three = variables above in a function defined in a file other than desktop.el, an= d lexical-binding is t in that other file, then those variables will be b= ound lexically, not dynamically. This was causing an inexplicable bug in my code that uses functions from = desktop.el, that I could figure out how to fix only by using setq instead= of =C2=ABlet=C2=BB for those variables. Then today I happened to read th= e source code for defvar and discovered what's really going on. I can fix= my bug without setq, by instead using defvar (without INITVALUE) before = =C2=ABlet=C2=BB. If the docstring for defvar were true (i.e. top-level de= fvar declares special (even if INITVALUE is missing)), then I wouldn't ne= ed to use defvar before the =C2=ABlet=C2=BB, because desktop.el would hav= e already made those variables special. It's no problem for me to use def= var before the =C2=ABlet=C2=BB, but the docstring should say what defvar = really does, so people know it's necessary in this case. Also, CL doesn't behave this way. E.g. In Elisp (in Emacs 24.4): (setq lexical-binding t) (let ((foo 'bar)) (defvar foo) foo) =E2=86=92 bar (let ((foo 'baz)) (makunbound 'foo) foo) =E2=86=92 baz But in CL: (let ((foo 'bar)) (defvar foo) foo) =E2=86=92 bar (let ((foo 'baz)) (makunbound 'foo) foo) =E2=86=92 error: foo is unbound In Elisp, both let-bindings are lexical, but in CL, the second let-bindin= g is dynamic. What's the purpose of Elisp behaving this way? Is it just to enable local= use of dynamic variables (for implicit arguments and return values of fu= nctions) without having to clutter the globals with otherwise-unneeded sp= ecial variables? If so, a cleaner way to do it would be with a dynamic-le= t special form, rather than a weirdly-behaving defvar. Also, in Elisp: (setq lexical-binding t) (let ((foo 0)) (defvar foo) (let ((foo 1)) foo)) =E2=86=92 0 That's because eval_sub in eval.c looks up the variable in the lexical en= vironment using only Fassq, without first using Fmemq to check for a loca= l dynamic binding. Is that behavior actually correct?