unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* gv-exander for 'list'
@ 2019-03-19 16:39 Michael Heerdegen
  2019-03-19 18:41 ` Stefan Monnier
  0 siblings, 1 reply; 7+ messages in thread
From: Michael Heerdegen @ 2019-03-19 16:39 UTC (permalink / raw)
  To: Emacs Development

[-- Attachment #1: Type: text/plain, Size: 140 bytes --]

Hello,

while I was trying to understand the "debatable" gv expanders (`if',
`cond' etc.) I had the idea of adding an expander for `list':


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-lisp-emacs-lisp-gv.el-Add-gv-expander-for-list.patch --]
[-- Type: text/x-diff, Size: 1448 bytes --]

From 85f6743d1f5f32305da298717279491067dddb0f Mon Sep 17 00:00:00 2001
From: Michael Heerdegen <michael_heerdegen@web.de>
Date: Tue, 19 Mar 2019 17:31:53 +0100
Subject: [PATCH] * lisp/emacs-lisp/gv.el: Add gv-expander for `list'

---
 lisp/emacs-lisp/gv.el | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/lisp/emacs-lisp/gv.el b/lisp/emacs-lisp/gv.el
index 4ea3ce84fc..4737f8df90 100644
--- a/lisp/emacs-lisp/gv.el
+++ b/lisp/emacs-lisp/gv.el
@@ -519,6 +519,21 @@ gv-delay-error
                               ,(funcall asetter `(car ,v))
                               ,(funcall dsetter `(cdr ,v)))))))))

+(put 'list 'gv-expander
+     (lambda (do &rest elt-places)
+       ;; FIXME: when using this with letf people would expect this to
+       ;; create local bindings
+       (let ((getters+setters
+              (mapcar (lambda (place)
+                        (gv-get place (lambda (g s) (cons g s))))
+                      elt-places)))
+         (funcall do `(list ,@(mapcar #'car getters+setters))
+                  (lambda (v)
+                    (macroexp-let2 macroexp-copyable-p v v
+                      (macroexp-progn
+                       (mapcar (lambda (x) (funcall (cdr x) `(pop ,v)))
+                               getters+setters))))))))
+
 (put 'logand 'gv-expander
      (lambda (do place &rest masks)
        (gv-letplace (getter setter) place
--
2.20.1


[-- Attachment #3: Type: text/plain, Size: 202 bytes --]


It could be an alternative to `progv', where the variable list isn't
computed dynamically (the values list still is), but OTOH allows
generalized variables.  Would that make sense?


Thanks,

Michael.

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

* Re: gv-exander for 'list'
  2019-03-19 16:39 gv-exander for 'list' Michael Heerdegen
@ 2019-03-19 18:41 ` Stefan Monnier
  2019-03-19 22:34   ` Michael Heerdegen
  0 siblings, 1 reply; 7+ messages in thread
From: Stefan Monnier @ 2019-03-19 18:41 UTC (permalink / raw)
  To: emacs-devel

> while I was trying to understand the "debatable" gv expanders (`if',
> `cond' etc.) I had the idea of adding an expander for `list':

[ Ahem!  `if` and `cond` aren't in the "debatable" category.
  `cons` and logand` are!
  BTW, I think the unifying "feature" of the debatable category is that
  these GVs impose constraints on the value that can be assigned.  ]

> +(put 'list 'gv-expander
> +     (lambda (do &rest elt-places)
> +       ;; FIXME: when using this with letf people would expect this to
> +       ;; create local bindings

Not sure what you mean by this FIXME.

> +       (let ((getters+setters
> +              (mapcar (lambda (place)
> +                        (gv-get place (lambda (g s) (cons g s))))
> +                      elt-places)))
> +         (funcall do `(list ,@(mapcar #'car getters+setters))
> +                  (lambda (v)
> +                    (macroexp-let2 macroexp-copyable-p v v
> +                      (macroexp-progn
> +                       (mapcar (lambda (x) (funcall (cdr x) `(pop ,v)))
> +                               getters+setters))))))))

This expands to incorrect code: (macroexpand '(setf (list a b) x))
You should probably replace `macroexp-copyable-p` with `ignore`.

> It could be an alternative to `progv', where the variable list isn't
> computed dynamically (the values list still is), but OTOH allows
> generalized variables.  Would that make sense?

I don't see why it wouldn't do what you say, but I'm not sure how often
that would be usable (and preferable to an alternative).

Also, as a replacement for cl-progv it will suffer from the fact that
instead of using the built-in C code for dynamic scoping, it will mimic
it via unwind-protect, which means that it will misbehave in the
presence of buffer-local values:

    (let ((buf (current-buffer)))
      (with-temp-buffer
        (setq default-directory "/bar/")
        (cl-letf (((list default-directory) '("/foo/")))
          (set-buffer buf))))

There are a few other similar cases that we slowly
patched over the years ;-)

[ Note that it's one of the reasons (beside efficiency) why `cl-letf` is
  careful to treat simple variables specially, thus taking advantage of
  the built-in handling of dynamically-scoped vars.  ]

        Stefan




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

* Re: gv-exander for 'list'
  2019-03-19 18:41 ` Stefan Monnier
@ 2019-03-19 22:34   ` Michael Heerdegen
  2019-03-20  0:38     ` Stefan Monnier
  0 siblings, 1 reply; 7+ messages in thread
From: Michael Heerdegen @ 2019-03-19 22:34 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

> > +(put 'list 'gv-expander
> > +     (lambda (do &rest elt-places)
> > +       ;; FIXME: when using this with letf people would expect this to
> > +       ;; create local bindings
>
> Not sure what you mean by this FIXME.

I mean that something like

(cl-letf (((list a) (list 1)))
  a)

errors unless variable a is already bound.

> > +       (let ((getters+setters
> > +              (mapcar (lambda (place)
> > +                        (gv-get place (lambda (g s) (cons g s))))
> > +                      elt-places)))
> > +         (funcall do `(list ,@(mapcar #'car getters+setters))
> > +                  (lambda (v)
> > +                    (macroexp-let2 macroexp-copyable-p v v
> > +                      (macroexp-progn
> > + (mapcar (lambda (x) (funcall (cdr x) `(pop ,v)))
> > +                               getters+setters))))))))
>
> This expands to incorrect code: (macroexpand '(setf (list a b) x))
> You should probably replace `macroexp-copyable-p` with `ignore`.

Indeed, thanks.

> > It could be an alternative to `progv', where the variable list isn't
> > computed dynamically (the values list still is), but OTOH allows
> > generalized variables.  Would that make sense?
>
> I don't see why it wouldn't do what you say, but I'm not sure how often
> that would be usable (and preferable to an alternative).
>
> Also, as a replacement for cl-progv it will suffer from the fact that
> instead of using the built-in C code for dynamic scoping, it will mimic
> it via unwind-protect, which means that it will misbehave in the
> presence of buffer-local values:
>
>     (let ((buf (current-buffer)))
>       (with-temp-buffer
>         (setq default-directory "/bar/")
>         (cl-letf (((list default-directory) '("/foo/")))
>           (set-buffer buf))))
>
> There are a few other similar cases that we slowly
> patched over the years ;-)
>
> [ Note that it's one of the reasons (beside efficiency) why `cl-letf` is
>   careful to treat simple variables specially, thus taking advantage of
>   the built-in handling of dynamically-scoped vars.  ]

I think I just would want simple variables in `list' treated the same
way.


Michael.



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

* Re: gv-exander for 'list'
  2019-03-19 22:34   ` Michael Heerdegen
@ 2019-03-20  0:38     ` Stefan Monnier
  2019-03-20 18:15       ` Michael Heerdegen
  0 siblings, 1 reply; 7+ messages in thread
From: Stefan Monnier @ 2019-03-20  0:38 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

>> > +(put 'list 'gv-expander
>> > +     (lambda (do &rest elt-places)
>> > +       ;; FIXME: when using this with letf people would expect this to
>> > +       ;; create local bindings
>>
>> Not sure what you mean by this FIXME.
>
> I mean that something like
>
> (cl-letf (((list a) (list 1)))
>   a)
>
> errors unless variable a is already bound.

Oh, right, that too.

> I think I just would want simple variables in `list' treated the same way.

Indeed, but the GV infrastructure doesn't offer any way for cl-letf to
do that correctly (short of ad-hoc hacks that special-case `list`).

There are other GVs where `cl-letf` would like to do something a bit
different than just "get...set".  E.g. for buffer-modified-p the setter
should be restore-buffer-modified-p when used in `cl-letf`.  I haven't
spent much time trying to solve this problem, tho, because `cl-letf` is
not that important in my opinion.

Personally, I wrote "debatable" for the `cons` and `logand` gv-expanders
but I think they're undesirable.  Of the two the `logand` is
probably the most useful one (because `logand` can be considered as
a form of projection, like `aref`, `nth`, `car`, ... whereas `cons`
really doesn't fit the pattern).


        Stefan



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

* Re: gv-exander for 'list'
  2019-03-20  0:38     ` Stefan Monnier
@ 2019-03-20 18:15       ` Michael Heerdegen
  2019-03-20 19:06         ` Stefan Monnier
  0 siblings, 1 reply; 7+ messages in thread
From: Michael Heerdegen @ 2019-03-20 18:15 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

> > I mean that something like
> >
> > (cl-letf (((list a) (list 1)))
> >   a)
> >
> > errors unless variable a is already bound.
>
> Oh, right, that too.

Ok, then let's forget the idea.

BTW, in

> + (funcall do `(list ,@(mapcar #'car getters+setters))
> +          (lambda (v)
> +            (macroexp-let2 macroexp-copyable-p v v
> +              (macroexp-progn
> +               (mapcar (lambda (x) (funcall (cdr x) `(pop ,v)))
> +                       getters+setters))))))))

is there a trick to avoid the redundant macroexp-progn in such a
situation?  macroexp-let2 only accept a body as &rest, but since it's a
macro I can't `apply' it.


Thanks,

Michael.



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

* Re: gv-exander for 'list'
  2019-03-20 18:15       ` Michael Heerdegen
@ 2019-03-20 19:06         ` Stefan Monnier
  2019-03-20 20:03           ` Michael Heerdegen
  0 siblings, 1 reply; 7+ messages in thread
From: Stefan Monnier @ 2019-03-20 19:06 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: emacs-devel

>> + (funcall do `(list ,@(mapcar #'car getters+setters))
>> +          (lambda (v)
>> +            (macroexp-let2 macroexp-copyable-p v v
>> +              (macroexp-progn
>> +               (mapcar (lambda (x) (funcall (cdr x) `(pop ,v)))
>> +                       getters+setters))))))))
> is there a trick to avoid the redundant macroexp-progn in such a
> situation?  macroexp-let2 only accept a body as &rest, but since it's a
> macro I can't `apply' it.

The redundant `progn` should be removed by `macroexp-let2`, but
otherwise I can't see how you could avoid the `progn` while still
using `macroexp-let2`.


        Stefan



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

* Re: gv-exander for 'list'
  2019-03-20 19:06         ` Stefan Monnier
@ 2019-03-20 20:03           ` Michael Heerdegen
  0 siblings, 0 replies; 7+ messages in thread
From: Michael Heerdegen @ 2019-03-20 20:03 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Stefan Monnier <monnier@iro.umontreal.ca> writes:

> The redundant `progn` should be removed by `macroexp-let2`, but
> otherwise I can't see how you could avoid the `progn` while still
> using `macroexp-let2`.

Oh, yes, I see now that macroexp-let2 wraps the BODY forms in
macroexp-progn anyway, so macroexp-let2 indeed returns the same code.


Thanks,

Michael.



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

end of thread, other threads:[~2019-03-20 20:03 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-19 16:39 gv-exander for 'list' Michael Heerdegen
2019-03-19 18:41 ` Stefan Monnier
2019-03-19 22:34   ` Michael Heerdegen
2019-03-20  0:38     ` Stefan Monnier
2019-03-20 18:15       ` Michael Heerdegen
2019-03-20 19:06         ` Stefan Monnier
2019-03-20 20:03           ` Michael Heerdegen

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).