From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: "Stefan Monnier" Newsgroups: gmane.emacs.devel Subject: Re: let vs. buffer local bindings Date: Fri, 10 May 2002 15:15:28 -0400 Sender: emacs-devel-admin@gnu.org Message-ID: <200205101915.g4AJFSk21990@rum.cs.yale.edu> References: <5xy9esxewc.fsf@kfs2.cua.dk> NNTP-Posting-Host: localhost.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: main.gmane.org 1021058310 20016 127.0.0.1 (10 May 2002 19:18:30 GMT) X-Complaints-To: usenet@main.gmane.org NNTP-Posting-Date: Fri, 10 May 2002 19:18:30 +0000 (UTC) Cc: emacs-devel@gnu.org, Dave Pearson , Gareth Owen Return-path: Original-Received: from quimby.gnus.org ([80.91.224.244]) by main.gmane.org with esmtp (Exim 3.33 #1 (Debian)) id 176FuQ-0005Cj-00 for ; Fri, 10 May 2002 21:18:30 +0200 Original-Received: from fencepost.gnu.org ([199.232.76.164]) by quimby.gnus.org with esmtp (Exim 3.12 #1 (Debian)) id 176G3e-0001VI-00 for ; Fri, 10 May 2002 21:28:02 +0200 Original-Received: from localhost ([127.0.0.1] helo=fencepost.gnu.org) by fencepost.gnu.org with esmtp (Exim 3.34 #1 (Debian)) id 176FuG-0004YQ-00; Fri, 10 May 2002 15:18:20 -0400 Original-Received: from rum.cs.yale.edu ([128.36.229.169]) by fencepost.gnu.org with esmtp (Exim 3.34 #1 (Debian)) id 176Fro-0004RV-00 for ; Fri, 10 May 2002 15:15:49 -0400 Original-Received: (from monnier@localhost) by rum.cs.yale.edu (8.11.6/8.11.6) id g4AJFSk21990; Fri, 10 May 2002 15:15:28 -0400 X-Mailer: exmh version 2.4 06/23/2000 with nmh-1.0.4 Original-To: storm@cua.dk (Kim F. Storm) Errors-To: emacs-devel-admin@gnu.org X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.0.9 Precedence: bulk List-Help: List-Post: List-Subscribe: , List-Id: Emacs development discussions. List-Unsubscribe: , List-Archive: Xref: main.gmane.org gmane.emacs.devel:3816 X-Report-Spam: http://spam.gmane.org/gmane.emacs.devel:3816 > Gareth Owen has found a peculiar > interference between let and buffer local bindings. Gerd has fixed one of the bad interactions between buffer-local and let-bound variables, but we can't fix them all. Or at least, I think that fixing them such that there's a clear semantics will mean changing the current semantics in a way that would break compatibility. Currently, (let ((x val)) body) is similar to something like (push x) (unwind-protect (progn (setq x val) body) (setq x (pop))) And similarly, `set-buffer' works by changing the "current" value of all its local variables. So in your example you get: (progn `foo' is local in the buffer. Its global value is 1. (setq foo 0) The local value is now 0. (list foo So you get 0 for the first elem of the list. (let ((foo 2) (buf (generate-new-buffer "baz"))) the current value of foo is now set to 2 (note that the current value right now is the value in the local buffer). (set-buffer buf) foo) `foo' because foo is local in the current buffer but not in `baz', its current value is changed to the one in `baz' which is the global value (i.e. 1) so you get 1 as the second elem of the list. At the end, the buffer-local value in the original buffer is reset from 2 back to 0. Before Gerd's fix (i.e. in Emacs<21) it would have "reset" the current value instead, thus changing the global value from 1 to 0. (let ((foo 3) (buf (generate-new-buffer "baz"))) Now the current value (i.e. the global value) is set to 3. (set-buffer buf) foo) When switching buffer, we're now sitching between two buffers where foo is both times global, so foo's value is not changed. We thus get 3 and at the end, the global value is reset to 1. (let ((foo 4) (buf (generate-new-buffer "baz"))) (set-buffer buf) foo))) I think by now you now how this works. These semantics are pretty ugly and basically "defined by the implementation" with a very operational feeling to them. Not declarative at all. Another problem with it is that it relies on assignments, which means that it would interact very poorly with concurrency (while inside (let ((newvar 1)) ...) another thread would see that the global value of `newvar' is 1 instead of being unbound. I think a good practice is to use with-current-buffer rather than set-buffer because it avoids the most surprising cases (such as the one in your example). Stefan