unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
@ 2017-07-12 21:53 Michael Heerdegen
  2017-07-13  0:21 ` npostavs
  0 siblings, 1 reply; 14+ messages in thread
From: Michael Heerdegen @ 2017-07-12 21:53 UTC (permalink / raw)
  To: 27674


Hello,

The way `cl-progv' is implemented, we have some strange effects
happening to closures in the body.  For example, with lexical-binding
on,

(let ((x 0))
  (cl-progv (list 'x) (list 1)
    (funcall (lambda () x))))

yields 0, and

(cl-progv (list 'x) (list 1)
  (funcall (lambda () x)))

yields 1.  That isn't consistent (FWIW I would expect `1' in both
cases).


TIA,

Michael.



In GNU Emacs 26.0.50 (build 7, x86_64-pc-linux-gnu, GTK+ Version 3.22.16)
 of 2017-07-12 built on drachen
Repository revision: dde7f2d48b53996bdf767a8cf91aafc2e10add23
Windowing system distributor 'The X.Org Foundation', version 11.0.11903000
System Description:	Debian GNU/Linux testing (buster)






^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-12 21:53 bug#27674: 26.0.50; cl-progv: strange scoping due to implementation Michael Heerdegen
@ 2017-07-13  0:21 ` npostavs
  2017-07-13  0:36   ` Michael Heerdegen
  0 siblings, 1 reply; 14+ messages in thread
From: npostavs @ 2017-07-13  0:21 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 27674

Michael Heerdegen <michael_heerdegen@web.de> writes:

> The way `cl-progv' is implemented, we have some strange effects
> happening to closures in the body.  For example, with lexical-binding
> on,
>
> (let ((x 0))
>   (cl-progv (list 'x) (list 1)
>     (funcall (lambda () x))))
>
> yields 0, and
>
> (cl-progv (list 'x) (list 1)
>   (funcall (lambda () x)))
>
> yields 1.  That isn't consistent (FWIW I would expect `1' in both
> cases).

IMO, this is a bug in your program, this yields 1:

(progn
  (defvar x)
  (let ((x 0))
    (cl-progv (list 'x) (list 1)
      (funcall (lambda () x)))))

Note that your second expression gives a compile warning (it's also
missing a defvar, though it happens to give the result you expect even
without that):

27674.el:10:25:Warning: reference to free variable ‘x’





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-13  0:21 ` npostavs
@ 2017-07-13  0:36   ` Michael Heerdegen
  2017-07-13  0:50     ` npostavs
  0 siblings, 1 reply; 14+ messages in thread
From: Michael Heerdegen @ 2017-07-13  0:36 UTC (permalink / raw)
  To: npostavs; +Cc: 27674

npostavs@users.sourceforge.net writes:

> Michael Heerdegen <michael_heerdegen@web.de> writes:
>
> > The way `cl-progv' is implemented, we have some strange effects
> > happening to closures in the body.  For example, with lexical-binding
> > on,
> >
> > (let ((x 0))
> >   (cl-progv (list 'x) (list 1)
> >     (funcall (lambda () x))))
> >
> > yields 0, and
> >
> > (cl-progv (list 'x) (list 1)
> >   (funcall (lambda () x)))
> >
> > yields 1.  That isn't consistent (FWIW I would expect `1' in both
> > cases).
>
> IMO, this is a bug in your program

Why?

>, this yields 1:
>
> (progn
>   (defvar x)
>   (let ((x 0))
>     (cl-progv (list 'x) (list 1)
>       (funcall (lambda () x)))))

Sure, but that's something different.  I didn't want a special variable
in my example.  The doc of `cl-progv' doesn't mention that the symbols
must correspond to special variables.  Do I miss something?


Michael.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-13  0:36   ` Michael Heerdegen
@ 2017-07-13  0:50     ` npostavs
  2017-07-13  1:11       ` Michael Heerdegen
  0 siblings, 1 reply; 14+ messages in thread
From: npostavs @ 2017-07-13  0:50 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 27674

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Sure, but that's something different.  I didn't want a special variable
> in my example.  The doc of `cl-progv' doesn't mention that the symbols
> must correspond to special variables.  Do I miss something?

Oh, you expect cl-progv to bind lexically?  I interpret the first
sentence in its docstring to mean that cl-progv does dynamic binding,
not lexical binding.  Note also, that if it did bind lexically, we would
not be able to compile the body form.

(defmacro cl-progv (symbols values &rest body)
  "Bind SYMBOLS to VALUES dynamically in BODY.
                          ^^^^^^^^^^^





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-13  0:50     ` npostavs
@ 2017-07-13  1:11       ` Michael Heerdegen
  2017-07-13  1:54         ` npostavs
  0 siblings, 1 reply; 14+ messages in thread
From: Michael Heerdegen @ 2017-07-13  1:11 UTC (permalink / raw)
  To: npostavs; +Cc: 27674

npostavs@users.sourceforge.net writes:

> Michael Heerdegen <michael_heerdegen@web.de> writes:
>
> > Sure, but that's something different.  I didn't want a special variable
> > in my example.  The doc of `cl-progv' doesn't mention that the symbols
> > must correspond to special variables.  Do I miss something?
>
> Oh, you expect cl-progv to bind lexically?

Eh - no.  Maybe I have a wrong mental model.  I thought that the free
variable `x' in the lambda is (also) in the scope of the dynamical
binding created by `progv', and because that binding is established
inside the `let' establishing the lexical binding of `x', it would
shadow the lexical binding.

Why does the lambda still refer to the lexical binding?

FWIW I see that this example shows the same behavior:

#+begin_src emacs-lisp
(setq x 'foo)

(let ((x 0))
  (cl-letf (((symbol-value 'x) 1))
    (funcall (lambda () x))))
#+end_src
==> 0

Does a lexical binding always beat a dynamical one?


Michael.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-13  1:11       ` Michael Heerdegen
@ 2017-07-13  1:54         ` npostavs
  2017-07-13  2:15           ` Michael Heerdegen
  0 siblings, 1 reply; 14+ messages in thread
From: npostavs @ 2017-07-13  1:54 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 27674

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Eh - no.  Maybe I have a wrong mental model.  I thought that the free
> variable `x' in the lambda is (also) in the scope of the dynamical
> binding created by `progv', and because that binding is established
> inside the `let' establishing the lexical binding of `x', it would
> shadow the lexical binding.

Oh, you want lexical and dynamic binding on the same variable?  I think
the answer is "don't do that".

> Why does the lambda still refer to the lexical binding?

Maybe it would be more obvious if we wrote it like this:

    (let ((x 0))
      (cl-progv (list (intern (read-string "Enter var: "))) (list 1)
        (funcall (lambda () x))))

Clearly the inner x must refer to the lexical let-binding, right?  Even
if the user happens to enter `x' at the prompt this remains true.

> Does a lexical binding always beat a dynamical one?

Yes, lexical analysis is performed first and then the names are thrown
away, so you can't even tell when the "same" variable has been
dynamically bound as well.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-13  1:54         ` npostavs
@ 2017-07-13  2:15           ` Michael Heerdegen
  2017-07-13  2:41             ` npostavs
  2017-07-19 13:55             ` Stefan Monnier
  0 siblings, 2 replies; 14+ messages in thread
From: Michael Heerdegen @ 2017-07-13  2:15 UTC (permalink / raw)
  To: npostavs; +Cc: 27674

npostavs@users.sourceforge.net writes:

> > Why does the lambda still refer to the lexical binding?
>
> Maybe it would be more obvious if we wrote it like this:
>
>     (let ((x 0))
>       (cl-progv (list (intern (read-string "Enter var: "))) (list 1)
>         (funcall (lambda () x))))
>
> Clearly the inner x must refer to the lexical let-binding, right?  Even
> if the user happens to enter `x' at the prompt this remains true.

Not an argument per se, because with lexical binding mode off, you can
surely do that.

> > Does a lexical binding always beat a dynamical one?
>
> Yes, lexical analysis is performed first and then the names are thrown
> away, so you can't even tell when the "same" variable has been
> dynamically bound as well.

Ok, this is the part I was clearly missing, thanks.  I'll have a look if
the documentation tells something like this (it should be spelled out
somewhere).

Then I guess you can close this report.


Thank you,

Michael.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-13  2:15           ` Michael Heerdegen
@ 2017-07-13  2:41             ` npostavs
  2017-07-13 14:40               ` Roland Winkler
  2017-07-14 16:00               ` Michael Heerdegen
  2017-07-19 13:55             ` Stefan Monnier
  1 sibling, 2 replies; 14+ messages in thread
From: npostavs @ 2017-07-13  2:41 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 27674

tags 27674 notabug wontfix
close 27674
quit

Michael Heerdegen <michael_heerdegen@web.de> writes:

> npostavs@users.sourceforge.net writes:
>
>> > Why does the lambda still refer to the lexical binding?
>>
>> Maybe it would be more obvious if we wrote it like this:
>>
>>     (let ((x 0))
>>       (cl-progv (list (intern (read-string "Enter var: "))) (list 1)
>>         (funcall (lambda () x))))
>>
>> Clearly the inner x must refer to the lexical let-binding, right?  Even
>> if the user happens to enter `x' at the prompt this remains true.
>
> Not an argument per se, because with lexical binding mode off, you can
> surely do that.

Of course, if `x' is a dynamic variable (e.g., if you use (defvar x) or
you don't have lexical binding enabled) then the inner x refers to the
dynamic binding (again, regardless of what the user enters at the
prompt).

>> > Does a lexical binding always beat a dynamical one?
>>
>> Yes, lexical analysis is performed first and then the names are thrown
>> away, so you can't even tell when the "same" variable has been
>> dynamically bound as well.
>
> Ok, this is the part I was clearly missing, thanks.  I'll have a look if
> the documentation tells something like this (it should be spelled out
> somewhere).

That explanation might be a little bit "infected" by my knowledge of how
the compiler implements lexical binding, the manual carefully talks only
in terms of the "evaluator":

       Here is how lexical binding works.  Each binding construct defines a
    "lexical environment", specifying the variables that are bound within
    the construct and their local values.  When the Lisp evaluator wants
    the current value of a variable, it looks first in the lexical environment. 
                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-13  2:41             ` npostavs
@ 2017-07-13 14:40               ` Roland Winkler
  2017-07-13 15:07                 ` Noam Postavsky
  2017-07-14 16:00               ` Michael Heerdegen
  1 sibling, 1 reply; 14+ messages in thread
From: Roland Winkler @ 2017-07-13 14:40 UTC (permalink / raw)
  To: 27674

On Wed, Jul 12 2017, npostavs@users.sourceforge.net wrote:
>>> > Why does the lambda still refer to the lexical binding?
>>>
>>> Maybe it would be more obvious if we wrote it like this:
>>>
>>>     (let ((x 0))
>>>       (cl-progv (list (intern (read-string "Enter var: "))) (list 1)
>>>         (funcall (lambda () x))))
>>>
>>> Clearly the inner x must refer to the lexical let-binding, right?  Even
>>> if the user happens to enter `x' at the prompt this remains true.
>>
>> Not an argument per se, because with lexical binding mode off, you can
>> surely do that.
>
> Of course, if `x' is a dynamic variable (e.g., if you use (defvar x) or
> you don't have lexical binding enabled) then the inner x refers to the
> dynamic binding (again, regardless of what the user enters at the
> prompt).

I am not sure I can follow this thread:

The docstring of cl-progv says

  Bind SYMBOLS to VALUES dynamically in BODY.

But I am not sure whether this statement correctly reflects the actual
code of cl-progv: cl-progv evaluates a let form at runtime, but it is
not up to cl-progv to ensure dynamical binding.  I believe it depends on
whether lexical binding is on or off whether the code currently used by
cl-progv uses dynamical binding or lexical binding.

So it is my ignorant guess that a consistent behavior of cl-progv with
lexical binding on or off requires that the bindings of SYMBOLS to
VALUES is also passed to `eval' as a second arg.  From the docstring of eval:

  LEXICAL can also be an actual lexical environment, in the form of an
  alist mapping symbols to their value.






^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-13 14:40               ` Roland Winkler
@ 2017-07-13 15:07                 ` Noam Postavsky
  2017-07-14 14:20                   ` Michael Heerdegen
  0 siblings, 1 reply; 14+ messages in thread
From: Noam Postavsky @ 2017-07-13 15:07 UTC (permalink / raw)
  To: Roland Winkler; +Cc: 27674

On Thu, Jul 13, 2017 at 10:40 AM, Roland Winkler <winkler@gnu.org> wrote:
>
> The docstring of cl-progv says
>
>   Bind SYMBOLS to VALUES dynamically in BODY.
>
> But I am not sure whether this statement correctly reflects the actual
> code of cl-progv: cl-progv evaluates a let form at runtime, but it is
> not up to cl-progv to ensure dynamical binding.  I believe it depends on
> whether lexical binding is on or off whether the code currently used by
> cl-progv uses dynamical binding or lexical binding.

No, because cl-progv omits the second argument to `eval', which is the
same as passing nil. This guarantees the evaluated let-bindings are
dynamic bindings.

> So it is my ignorant guess that a consistent behavior of cl-progv with
> lexical binding on or off requires that the bindings of SYMBOLS to
> VALUES is also passed to `eval' as a second arg.

Doing this would mean that cl-progv would always bind lexically. We
could, in theory, change cl-progv to be that way, but it would be
backwards incompatible with previous Emacs versions, and with Common
Lisp.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-13 15:07                 ` Noam Postavsky
@ 2017-07-14 14:20                   ` Michael Heerdegen
  2017-07-15 20:46                     ` Roland Winkler
  0 siblings, 1 reply; 14+ messages in thread
From: Michael Heerdegen @ 2017-07-14 14:20 UTC (permalink / raw)
  To: Noam Postavsky; +Cc: 27674, Roland Winkler

Noam Postavsky <npostavs@users.sourceforge.net> writes:

> > The docstring of cl-progv says
> >
> >   Bind SYMBOLS to VALUES dynamically in BODY.
> >
> > But I am not sure whether this statement correctly reflects the actual
> > code of cl-progv: cl-progv evaluates a let form at runtime, but it is
> > not up to cl-progv to ensure dynamical binding.  I believe it
> > depends on
> > whether lexical binding is on or off whether the code currently used by
> > cl-progv uses dynamical binding or lexical binding.
>
> No, because cl-progv omits the second argument to `eval', which is the
> same as passing nil. This guarantees the evaluated let-bindings are
> dynamic bindings.

Yes.  The second part of the answer is the creation of BODYFUN outside
of `eval': it ensures that free variables in the BODY refer to the outer
lexical environment regardless of `eval' being without performed with
lexical binding off.


Michael.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-13  2:41             ` npostavs
  2017-07-13 14:40               ` Roland Winkler
@ 2017-07-14 16:00               ` Michael Heerdegen
  1 sibling, 0 replies; 14+ messages in thread
From: Michael Heerdegen @ 2017-07-14 16:00 UTC (permalink / raw)
  To: npostavs; +Cc: 27674

npostavs@users.sourceforge.net writes:

> That explanation might be a little bit "infected" by my knowledge of how
> the compiler implements lexical binding, the manual carefully talks only
> in terms of the "evaluator":
>
>        Here is how lexical binding works.  Each binding construct defines a
>     "lexical environment", specifying the variables that are bound within
>     the construct and their local values.  When the Lisp evaluator wants
>     the current value of a variable, it looks first in the lexical environment. 
>                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I think that's good enough.


Michael.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-14 14:20                   ` Michael Heerdegen
@ 2017-07-15 20:46                     ` Roland Winkler
  0 siblings, 0 replies; 14+ messages in thread
From: Roland Winkler @ 2017-07-15 20:46 UTC (permalink / raw)
  To: 27674

For the records:

Essentially, Michael's examples expand to

(let ((x 0))
  (let ((fun (lambda () x)))
    (eval `(let ((x 1))
             (funcall ',fun)))))

(let ((fun (lambda () x)))
  (eval `(let ((x 1))
           (funcall ',fun))))

With dynamic binding, both examples return 1.  With lexical binding,
the compiler complains about

  reference to free variable `x'

in the 1st line of the 2nd example.  Then, the 1st example returns 0,
the 2nd example returns 1.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#27674: 26.0.50; cl-progv: strange scoping due to implementation
  2017-07-13  2:15           ` Michael Heerdegen
  2017-07-13  2:41             ` npostavs
@ 2017-07-19 13:55             ` Stefan Monnier
  1 sibling, 0 replies; 14+ messages in thread
From: Stefan Monnier @ 2017-07-19 13:55 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 27674, npostavs

> Ok, this is the part I was clearly missing, thanks.  I'll have a look if
> the documentation tells something like this (it should be spelled out
> somewhere).

FWIW, http://clhs.lisp.se/Body/s_progv.htm gives the following example:

 (let ((*x* 3)) 
    (progv '(*x*) '(4) 
      (list *x* (symbol-value '*x*)))) =>  (3 4)


-- Stefan





^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2017-07-19 13:55 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-07-12 21:53 bug#27674: 26.0.50; cl-progv: strange scoping due to implementation Michael Heerdegen
2017-07-13  0:21 ` npostavs
2017-07-13  0:36   ` Michael Heerdegen
2017-07-13  0:50     ` npostavs
2017-07-13  1:11       ` Michael Heerdegen
2017-07-13  1:54         ` npostavs
2017-07-13  2:15           ` Michael Heerdegen
2017-07-13  2:41             ` npostavs
2017-07-13 14:40               ` Roland Winkler
2017-07-13 15:07                 ` Noam Postavsky
2017-07-14 14:20                   ` Michael Heerdegen
2017-07-15 20:46                     ` Roland Winkler
2017-07-14 16:00               ` Michael Heerdegen
2017-07-19 13:55             ` Stefan Monnier

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).