emacs-devel
[Top][All Lists]
Advanced

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

Re: let vs. buffer local bindings


From: Stefan Monnier
Subject: Re: let vs. buffer local bindings
Date: Fri, 10 May 2002 15:15:28 -0400

> Gareth Owen <address@hidden> 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




reply via email to

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