Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.lisp > #32931 > unrolled thread

symbol-macros -vs- symbol setf-expander

Started byStefan Monnier <monnier@iro.umontreal.ca>
First post2019-05-21 15:46 -0400
Last post2019-05-22 18:47 -0700
Articles 20 on this page of 22 — 7 participants

Back to article view | Back to comp.lang.lisp


Contents

  symbol-macros -vs- symbol setf-expander Stefan Monnier <monnier@iro.umontreal.ca> - 2019-05-21 15:46 -0400
    Re: symbol-macros -vs- symbol setf-expander Kaz Kylheku <847-115-0292@kylheku.com> - 2019-05-21 21:25 +0000
      Re: symbol-macros -vs- symbol setf-expander Kaz Kylheku <847-115-0292@kylheku.com> - 2019-05-22 20:07 +0000
        Re: symbol-macros -vs- symbol setf-expander Stefan Monnier <monnier@iro.umontreal.ca> - 2019-05-30 15:40 -0400
          Re: symbol-macros -vs- symbol setf-expander Kaz Kylheku <847-115-0292@kylheku.com> - 2019-05-31 04:51 +0000
            Re: symbol-macros -vs- symbol setf-expander "Pascal J. Bourguignon" <pjb@informatimago.com> - 2019-05-31 16:38 +0200
              Re: symbol-macros -vs- symbol setf-expander Kaz Kylheku <847-115-0292@kylheku.com> - 2019-05-31 18:42 +0000
                Re: symbol-macros -vs- symbol setf-expander "Pascal J. Bourguignon" <pjb@informatimago.com> - 2019-05-31 21:24 +0200
                  Re: symbol-macros -vs- symbol setf-expander Kaz Kylheku <847-115-0292@kylheku.com> - 2019-06-01 02:34 +0000
                Re: symbol-macros -vs- symbol setf-expander Stefan Monnier <monnier@iro.umontreal.ca> - 2019-05-31 18:14 -0400
                  Re: symbol-macros -vs- symbol setf-expander Kaz Kylheku <847-115-0292@kylheku.com> - 2019-06-01 02:51 +0000
                    Re: symbol-macros -vs- symbol setf-expander Stefan Monnier <monnier@iro.umontreal.ca> - 2019-06-02 12:16 -0400
                      Re: symbol-macros -vs- symbol setf-expander Kaz Kylheku <847-115-0292@kylheku.com> - 2019-06-03 01:13 +0000
          Re: symbol-macros -vs- symbol setf-expander Stefan Monnier <monnier@iro.umontreal.ca> - 2019-05-31 18:04 -0400
    Re: symbol-macros -vs- symbol setf-expander "Pascal J. Bourguignon" <pjb@informatimago.com> - 2019-05-22 22:00 +0200
      Re: symbol-macros -vs- symbol setf-expander a.daniel.eliason@gmail.com - 2019-05-23 08:10 -0700
        Re: symbol-macros -vs- symbol setf-expander Spiros Bousbouras <spibou@gmail.com> - 2019-05-23 15:22 +0000
          Re: symbol-macros -vs- symbol setf-expander Dimitri Fontaine <dim@tapoueh.org> - 2019-05-23 18:02 +0200
            Re: symbol-macros -vs- symbol setf-expander Spiros Bousbouras <spibou@gmail.com> - 2019-05-23 16:51 +0000
          Re: symbol-macros -vs- symbol setf-expander Kaz Kylheku <847-115-0292@kylheku.com> - 2019-05-23 17:05 +0000
            Re: symbol-macros -vs- symbol setf-expander "Pascal J. Bourguignon" <pjb@informatimago.com> - 2019-05-23 19:26 +0200
    Re: symbol-macros -vs- symbol setf-expander smh <shaflich@gmail.com> - 2019-05-22 18:47 -0700

Page 1 of 2  [1] 2  Next page →


#32931 — symbol-macros -vs- symbol setf-expander

FromStefan Monnier <monnier@iro.umontreal.ca>
Date2019-05-21 15:46 -0400
Subjectsymbol-macros -vs- symbol setf-expander
Message-ID<jwv5zq3eh4i.fsf-monnier+comp.lang.lisp@gnu.org>
While discussing some extension to Elisp's `gv`, someone pointed out
that symbol-macros basically allow you to define a symbol as an
arbitrary "place", i.e. a kind of "place alias".

Yet, it's subtly different since

    (symbol-macrolet ((x (nth 5 (get-list)))) ...)
    
will re-execute (get-list) (and `nthcdr 5`) everytime `x` is used
without offering any easy way to take advantage of the setf-expander
infrastructure's ability to do those computations only once.

Does anyone here know of work related to this, e.g. discussions about
the detailed semantics of symbol-macrolet?


        Stefan

[toc] | [next] | [standalone]


#32932

FromKaz Kylheku <847-115-0292@kylheku.com>
Date2019-05-21 21:25 +0000
Message-ID<20190521125844.749@kylheku.com>
In reply to#32931
On 2019-05-21, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
> While discussing some extension to Elisp's `gv`, someone pointed out
> that symbol-macros basically allow you to define a symbol as an
> arbitrary "place", i.e. a kind of "place alias".
>
> Yet, it's subtly different since
>
>     (symbol-macrolet ((x (nth 5 (get-list)))) ...)
>     
> will re-execute (get-list) (and `nthcdr 5`) everytime `x` is used
> without offering any easy way to take advantage of the setf-expander
> infrastructure's ability to do those computations only once.
>
> Does anyone here know of work related to this, e.g. discussions about
> the detailed semantics of symbol-macrolet?

We have probably discussed this before, but in TXR Lisp I provide
something called placelet.

Placelet lexically binds symbols nicknames to places (similarly to
symbol macros), but with once-only semantics.

Ten second demo:

1> (expand '(placelet ((x (nth 5 (get-list))))
              (list x x (inc x))))
(let ((#:g0011 (nthcdr 5 (get-list))))
  (list (car #:g0011)
        (car #:g0011)
        (sys:rplaca #:g0011 (succ (car #:g0011)))))

Doc: http://nongnu.org/txr/txr-manpage.html#N-0393C970

Implementation: http://www.kylheku.com/cgit/txr/tree/share/txr/stdlib/place.tl

The bulk of the logic is in a macro called sys:placelet-1.

symbol-macrolet (called symacrolet) plays a key role, as do multiple
occurrences of macrolet; placelet isn't symacrolet, but its implementation
depends on it.

How the implementation works is that during expansion, placelet will
dynamically insert place definitions into the global hash table,
and then remove them (via unwind-protect).

The symbol in question, like in this case x, is then macro-replaced
with references to the temporary place.

The details are little tricky. The definitions use gensyms, so for instance
a place resembling (#:g0025) is defined whose "getter" might be
a form like (#:g0009) and whose setter is (#:g0010 <new-value>).
This #:g0025 is held in the tmp-place variable in sys:placelet-1
implementation.

The code is generated to instantiate the original (nth 5 ...) place.
The #:g0011 gensym is bound to (nthcdr 5 (get-list)). Together with this,
local macros are provided using gensym-ed names, so that the macrolet (#:g0009)
expands to the access (car #:g0011), and the macrolet form (#:g0010 whatever)
expands to (sys:rplaca #:g0011 whatever). These are the tmp-getter and
tmp-setter gensyms in sys:placelet-1.

The symbol x is then forcibly replaced with (#:g0025) throughout the code body
(full expand pass). At that stage the code fragments then looks like:

  (let ((#:g0011 (nthcdr 5 (get-list))))
    (macrolet ((#:g0009 () (car #:g0011)))
               (#:g0010 (val) (sys:rplaca #:g0011 val)))

      ;; tenative inner fragment: not finished yet:
      (list (#:g0025) (#:g0025) (sys:rplaca #:g0011 (succ (car #:g0011)))))

We can see that the simple accesses to x are replaced by just (#:g0025),
the form denoting the temporary place.  But where this is (#:g0025) is actually
used as a place, in the (inc ...) form, it is processed all the way to
(sys:rplaca #:g0011 ...) thanks to the workings of the temporary registration
of the (#:g0025) alias place.

Why (#:g0025) didn't get expanded into anything is that we have not defined
such a function or macro; we have (dynamically/locally) defined the place
(#:g0025) only, but for a fictitious form (#:g0025) that has no binding.

So now we are introducing that binding: we map (#:0025) to the access macrolet
(#:g0009):

  (macrolet ((#:g0025 (#:g0009))) ...)

Ending up with:

  (let ((#:g0011 (nthcdr 5 (get-list))))
    (macrolet ((#:g0009 () (car #:g0011)))  ;; <---- #:g0009 is here!
               (#:g0010 (val) (sys:rplaca #:g0011 val)))
      (macrolet ((#:g0025 (#:g0009))) ;; <--- referenced from here!
        (list (#:g0025) (#:g0025) (sys:rplaca #:g0011 (succ (car #:g0011))))))

This inner macrolet shakes out the (#:g0025) forms, replacing them with
(#:g0009) which immediately goes to the (car #:g0011) access form,
thanks to the outer macrolet.

-- 
TXR Programming Lanuage: http://nongnu.org/txr
Music DIY Mailing List:  http://www.kylheku.com/diy
ADA MP-1 Mailing List:   http://www.kylheku.com/mp1

[toc] | [prev] | [next] | [standalone]


#32935

FromKaz Kylheku <847-115-0292@kylheku.com>
Date2019-05-22 20:07 +0000
Message-ID<20190522125932.721@kylheku.com>
In reply to#32932
On 2019-05-21, Kaz Kylheku <847-115-0292@kylheku.com> wrote:
> On 2019-05-21, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>> While discussing some extension to Elisp's `gv`, someone pointed out
>> that symbol-macros basically allow you to define a symbol as an
>> arbitrary "place", i.e. a kind of "place alias".
>>
>> Yet, it's subtly different since
>>
>>     (symbol-macrolet ((x (nth 5 (get-list)))) ...)
>>     
>> will re-execute (get-list) (and `nthcdr 5`) everytime `x` is used
>> without offering any easy way to take advantage of the setf-expander
>> infrastructure's ability to do those computations only once.
>>
>> Does anyone here know of work related to this, e.g. discussions about
>> the detailed semantics of symbol-macrolet?
>
> We have probably discussed this before, but in TXR Lisp I provide
> something called placelet.

I found the prior discussion from 2016.

In your response, you said that this is like gv-letplace.

Thus, isn't gv-letplace what that user is looking for, instead of
symbol-macrolet?

symbol-macrolet cannot, and more importantly must not, be made to behave
like gv-letplace or placelet. symbol-macrolet must adhere to its
crystal-clear substitution model.

symbol-macrolet doesn't have any non-obvious "detailed semantics".
If a symbol is in an evaluated position, and lexically defined as a
symbol macro in that scope, then we replace it with its definition
(which is just a literal piece of structure with no parameters), and
then walk that definition to expand more macros. The End.

[toc] | [prev] | [next] | [standalone]


#32996

FromStefan Monnier <monnier@iro.umontreal.ca>
Date2019-05-30 15:40 -0400
Message-ID<jwv1s0fd9ej.fsf-monnier+comp.lang.lisp@gnu.org>
In reply to#32935
> In your response, you said that this is like gv-letplace.
> Thus, isn't gv-letplace what that user is looking for, instead of
> symbol-macrolet?

Rather it's just that the user pointed out the close relationship
between the two: you can implement a "place-alias" like your `letplace`
using `symbol-macrolet` and you can implement `symbol-macrolet` using
`letplace` (or so I presume, since we could do that with gv.el's
equivalent to your `letplace`).

So it made me wonder about this design space and whether maybe someone
has found something that's "more better" than either of the two.


        Stefan

[toc] | [prev] | [next] | [standalone]


#32998

FromKaz Kylheku <847-115-0292@kylheku.com>
Date2019-05-31 04:51 +0000
Message-ID<20190530214258.24@kylheku.com>
In reply to#32996
On 2019-05-30, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>> In your response, you said that this is like gv-letplace.
>> Thus, isn't gv-letplace what that user is looking for, instead of
>> symbol-macrolet?
>
> Rather it's just that the user pointed out the close relationship
> between the two: you can implement a "place-alias" like your `letplace`
> using `symbol-macrolet` and you can implement `symbol-macrolet` using
> `letplace` (or so I presume, since we could do that with gv.el's
> equivalent to your `letplace`).
>
> So it made me wonder about this design space and whether maybe someone
> has found something that's "more better" than either of the two.

By the way, I have discovered a quick and dirty "worse is better" way to
implement place aliases. It's not 100% correct for all possible places,
but it avoids requiring a "fully expand form" function. It also avoids
registering temporary place expanders over the scope of a form. Thus,
the approach is implementable in Common Lisp.

This is located here:

http://www.kylheku.com/cgit/lisp-snippets/tree/placelet.lisp

The define-setf-expander call in placelet1 is useless and can be
removed. The comments try to explain it.

The idea is when we want to bind a symbol S to a place (P),
what we do instead is obtain the setf expansion for (P) and
bind S to the access form for (P).

The issue is that by doing that, we ignore the store form.
We are relying on the store form being deducible from the
access form; i.e. that the access form is itself a place!

For instance, an expression like (nthcdr 3 foo) might
have an access form that looks like (cdr #:g0027).
The store form will be some (rplaca #:g0027 #:g0031) type
thing, but we ignore it.

Lucky for as, the (cdr #:g0027) form is a place, and so we can
make symbol macro S alias for this place.

The approach will fail if the place has an access form that
is itself not a place. But often, in fact usually I would say,
place access forms are places. So this little hack is fairly practical.

-- 
TXR Programming Lanuage: http://nongnu.org/txr
Music DIY Mailing List:  http://www.kylheku.com/diy
ADA MP-1 Mailing List:   http://www.kylheku.com/mp1

[toc] | [prev] | [next] | [standalone]


#32999

From"Pascal J. Bourguignon" <pjb@informatimago.com>
Date2019-05-31 16:38 +0200
Message-ID<m2r28e1yg8.fsf@despina.home>
In reply to#32998
Kaz Kylheku <847-115-0292@kylheku.com> writes:
>
> The define-setf-expander call in placelet1 is useless and can be
> removed. …
>
> Lucky for as, …
>
> The approach will fail …

Why would you consider an approach that will fail???

You really wish catastrophes on your users and childrens?

-- 
__Pascal J. Bourguignon__
http://www.informatimago.com

[toc] | [prev] | [next] | [standalone]


#33001

FromKaz Kylheku <847-115-0292@kylheku.com>
Date2019-05-31 18:42 +0000
Message-ID<20190531112949.658@kylheku.com>
In reply to#32999
On 2019-05-31, Pascal J. Bourguignon <pjb@informatimago.com> wrote:
> Kaz Kylheku <847-115-0292@kylheku.com> writes:
>>
>> The define-setf-expander call in placelet1 is useless and can be
>> removed. …
>>
>> Lucky for as, …
>>
>> The approach will fail …
>
> Why would you consider an approach that will fail???

It might fail to expand due to a form having no setf expansion being
used as a place. The placelet1 macro could be easily refined to detect
the situation, and provide an accurate diagnostic.

Why I would consider the approach is that it's very simple and
doesn't rely on any non-portable features.

At least it's worth noting/documenting: "if we are able to take a
certain requirement off the table, such and such highly simplified trick
is possible which handles the remaining requirements".

-- 
TXR Programming Lanuage: http://nongnu.org/txr
Music DIY Mailing List:  http://www.kylheku.com/diy
ADA MP-1 Mailing List:   http://www.kylheku.com/mp1

[toc] | [prev] | [next] | [standalone]


#33002

From"Pascal J. Bourguignon" <pjb@informatimago.com>
Date2019-05-31 21:24 +0200
Message-ID<m2muj21l7h.fsf@despina.home>
In reply to#33001
Kaz Kylheku <847-115-0292@kylheku.com> writes:

> On 2019-05-31, Pascal J. Bourguignon <pjb@informatimago.com> wrote:
>> Kaz Kylheku <847-115-0292@kylheku.com> writes:
>>>
>>> The define-setf-expander call in placelet1 is useless and can be
>>> removed. …
>>>
>>> Lucky for as, …
>>>
>>> The approach will fail …
>>
>> Why would you consider an approach that will fail???
>
> It might fail to expand due to a form having no setf expansion being
> used as a place. The placelet1 macro could be easily refined to detect
> the situation, and provide an accurate diagnostic.
>
> Why I would consider the approach is that it's very simple and
> doesn't rely on any non-portable features.
>
> At least it's worth noting/documenting: "if we are able to take a
> certain requirement off the table, such and such highly simplified trick
> is possible which handles the remaining requirements".

Furthermore, even if one call to get-setf-expansion gives you a result
such as (cdr #:foo), it doesn't mean that another call (notably with a
different &environment, but possibly for any reason (eg. different
optimization level, different run-time (OS where the same executable
runs), etc could have given a different expression for the getter, one
one hand, and on the other hand, there's also no guaranteed that the
expression given by the getter be actually a place.  Eg. you could get a
(kdr #:foo) instead, with no (setf kdr).

So basically, you need to perform the test, visually, which is worse
than automatically, but you can also do it automatically, but it all
comes down to perusing the section 5.1.2.2.
http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/sec_5-1-2-2.html
Ie. you must make the decision when you write the code.

-- 
__Pascal J. Bourguignon__
http://www.informatimago.com

[toc] | [prev] | [next] | [standalone]


#33005

FromKaz Kylheku <847-115-0292@kylheku.com>
Date2019-06-01 02:34 +0000
Message-ID<20190531190747.275@kylheku.com>
In reply to#33002
On 2019-05-31, Pascal J. Bourguignon <pjb@informatimago.com> wrote:
> Kaz Kylheku <847-115-0292@kylheku.com> writes:
>
>> On 2019-05-31, Pascal J. Bourguignon <pjb@informatimago.com> wrote:
>>> Kaz Kylheku <847-115-0292@kylheku.com> writes:
>>>>
>>>> The define-setf-expander call in placelet1 is useless and can be
>>>> removed. …
>>>>
>>>> Lucky for as, …
>>>>
>>>> The approach will fail …
>>>
>>> Why would you consider an approach that will fail???
>>
>> It might fail to expand due to a form having no setf expansion being
>> used as a place. The placelet1 macro could be easily refined to detect
>> the situation, and provide an accurate diagnostic.
>>
>> Why I would consider the approach is that it's very simple and
>> doesn't rely on any non-portable features.
>>
>> At least it's worth noting/documenting: "if we are able to take a
>> certain requirement off the table, such and such highly simplified trick
>> is possible which handles the remaining requirements".
>
> Furthermore, even if one call to get-setf-expansion gives you a result
> such as (cdr #:foo), it doesn't mean that another call (notably with a
> different &environment, but possibly for any reason (eg. different

Since CL doesn't have lexical setf-expansions, I think the enviornment
is only used (and necessary) for expanding macros. So that is to say, if
we want to obtain the setf expansion for some (foo) form, that form
could be a macrolet in the given context. get-setf-expansion has to
have the environment to resolve that.

> optimization level, different run-time (OS where the same executable
> runs), etc could have given a different expression for the getter, one
> one hand, and on the other hand, there's also no guaranteed that the
> expression given by the getter be actually a place.  Eg. you could get a
> (kdr #:foo) instead, with no (setf kdr).

Yes. I constructed "artificial" places exactly like this when testing
exploratory code in this area. This kind of place reliably produces
an error when the code is doing it wrong.

> So basically, you need to perform the test, visually, which is worse
> than automatically, but you can also do it automatically, but it all
> comes down to perusing the section 5.1.2.2.
> http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/sec_5-1-2-2.html
> Ie. you must make the decision when you write the code.

That doesn't really help.

The solution is conceptually very simple: we create a temporary place
like (#:G0025) which defers to the original place, using its access
and store forms (my code shows this). We bind the desired alias symbol S
to this (#:G0025) form and fully expand the code.  All S's get converted
to (#:G0025) and further, the expansion expand all (#:G0025) occurrences
that are used as places. The these expansions will use the correct
access and store form.  Occurrences of (#:G0025) that are not used as
places will stay unexpanded as (#:G0025); that symbol has no binding in
the function/operator space.  We then set up macrolet which binds
(#:G0025) to the access form, and inject that expanded code into that
macrolet; that code is our result.  Thus the remaining occurrences which
are pure accesses use the correct form also.

We can't do this in one phase, because if we simultaneously have a S
symbol macro expanding to (#:G0025) and a macrolet expanding (#:G0025)
to the access form, these expansions both happen first; every single S,
whether used as a place or not, is first reduced through (#:G0025) down
to the the access form and we are back to square one: uses of S as a
place are using the access form to deduce the store.

This is the very situation exemplified in the code: the (macrolet ...
(symbol-macrolet ...)) are combined into one piece of code with a single
expansion phase, and that is the reason why the define-setf-expander
form makes no difference, as the comment says. When the symbol-macrolet
and macrolet expand, nothing is left in the code that interacts with the
"proxy-place"; the code just contains references to the access form
of the original place.

Also, if we do fully expand the body code, then the proxy place
(#:G0025) is no longer needed. We would like to delete it to reduce
polution; alas, however, CL doesn't give us a portable way to undo the
effect of define-setf-expander. In some systems it's keyed to a property
of the symbol and whatnot.

-- 
TXR Programming Lanuage: http://nongnu.org/txr
Music DIY Mailing List:  http://www.kylheku.com/diy
ADA MP-1 Mailing List:   http://www.kylheku.com/mp1

[toc] | [prev] | [next] | [standalone]


#33004

FromStefan Monnier <monnier@iro.umontreal.ca>
Date2019-05-31 18:14 -0400
Message-ID<jwvy32mjn3p.fsf-monnier+comp.lang.lisp@gnu.org>
In reply to#33001
> Why I would consider the approach is that it's very simple and
> doesn't rely on any non-portable features.

I see the simplicity advantage, but not the portability advantage.
The way we'd implement it in Elisp, `placelet` would take the PLACE,
and extract from it a GETTER and a SETTER, and then do

    (symbol-macrolet ((X (synthetic-place GETTER SETTER))) ...)
    
where `synthetic-place` is a globally defined generalized variable (as
well as a macro that just returns its first arg).

In Elisp's `gv.el` this is very straightforward and portable and doesn't
require an assumption about the ability to re-construct the GETTER from
the SETTER.
I haven't tried to implement it in Common-Lisp, but I can't see any
obvious reason why the same approach wouldn't work just as well.


        Stefan

[toc] | [prev] | [next] | [standalone]


#33006

FromKaz Kylheku <847-115-0292@kylheku.com>
Date2019-06-01 02:51 +0000
Message-ID<20190531193523.418@kylheku.com>
In reply to#33004
On 2019-05-31, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>> Why I would consider the approach is that it's very simple and
>> doesn't rely on any non-portable features.
>
> I see the simplicity advantage, but not the portability advantage.
> The way we'd implement it in Elisp, `placelet` would take the PLACE,
> and extract from it a GETTER and a SETTER, and then do
>
>     (symbol-macrolet ((X (synthetic-place GETTER SETTER))) ...)
>     
> where `synthetic-place` is a globally defined generalized variable (as
> well as a macro that just returns its first arg).

This is the thing that fails in CL, which makes my code incorrect,
as commented.

Because if it's defined both as a macro that returns its first arg
(alias for the getter) and as a generalized place then that macro always
gets expanded, even if X is used as a place.

I.e. given

  (incf X)

X gets replaced by (synthetic-place G S), nicely enough. But that is
immediately recognized as a macro and taken to G. So we end up with

  (incf G) ;; the access form; S ignored

> In Elisp's `gv.el` this is very straightforward and portable and doesn't
> require an assumption about the ability to re-construct the GETTER from
> the SETTER.

Aha! Maybe in Elisp, the GV framework doesn't macro-expand a place if
that place is registered as a GV, even if it exists as a macro also.

I.e. it gives priority to the place?

I think I may have experimented with that order in TXR Lisp, but
for whatever reason, I ended up with the CL approach: always blindly
expand all macros, and only consider the form as a place once it
is fully expanded.

That could be the wrong approach: I will be certain to revisit that
design decision. I do seem to recall I had a good reason.

The opposite approach that I rejected does break macros in some ways.
Say there is some global (foo) place. Now in my code, suppose I have a
local macro (macrolet ((foo (...) ...))).  My code is not aware of the
global foo. This local foo *is* used as a place in my code: (incf
(foo)).  If this fails to expand my macro, but instead works with the
global (foo) generalized place (giving it priority), that is surprising,
and arguably breaks lexical scope: the macro should shadow everything.

-- 
TXR Programming Lanuage: http://nongnu.org/txr
Music DIY Mailing List:  http://www.kylheku.com/diy
ADA MP-1 Mailing List:   http://www.kylheku.com/mp1

[toc] | [prev] | [next] | [standalone]


#33007

FromStefan Monnier <monnier@iro.umontreal.ca>
Date2019-06-02 12:16 -0400
Message-ID<jwvmuj0j7z6.fsf-monnier+comp.lang.lisp@gnu.org>
In reply to#33006
> I.e. given
>
>   (incf X)
>
> X gets replaced by (synthetic-place G S), nicely enough. But that is
> immediately recognized as a macro and taken to G. So we end up with
>
>   (incf G) ;; the access form; S ignored

That sounds positively wrong: expansion of `incf` should happen before
we can know that (synthetic-place G S) is a Lisp expression (as opposed
to some data used by the macro some other way) that can be
macro-expanded, right?

> Aha! Maybe in Elisp, the GV framework doesn't macro-expand a place if
> that place is registered as a GV, even if it exists as a macro also.
> I.e. it gives priority to the place?

Exactly: the equivalent of get-setf-expansion first looks for a place
definition and only if that fails it falls back to looking for
a macro definition.

That seems to be the only good choice.  I'm surprised, Common-Lisp is
not more careful with such ordering.  It seems "out of character"
(seeing how it was careful to specify ordering of macroexpansion of
top-level `progn` forms, for example).

> I think I may have experimented with that order in TXR Lisp, but
> for whatever reason, I ended up with the CL approach: always blindly
> expand all macros, and only consider the form as a place once it
> is fully expanded.

But that choice simply disallows macros that are also setf-expander.

> That could be the wrong approach: I will be certain to revisit that
> design decision. I do seem to recall I had a good reason.

It can be tricky to get it right, admittedly (e.g. you need to use
macroexpand-1 so as to be able to check for the presence of
a setf-expander after each step of macroexpansion).  We recently fixed
a bug in symbol-macrolet in this regard where it macroexpanded its args
before substituting them into the body.

> The opposite approach that I rejected does break macros in some ways.
> Say there is some global (foo) place. Now in my code, suppose I have a
> local macro (macrolet ((foo (...) ...))).  My code is not aware of the
> global foo. This local foo *is* used as a place in my code: (incf
> (foo)).  If this fails to expand my macro, but instead works with the
> global (foo) generalized place (giving it priority), that is surprising,
> and arguably breaks lexical scope: the macro should shadow everything.

I think this is a much more general problem: the place you define is
associated to the *symbol* foo and not to its macro definition.
The same holds for places defined on functions: if you define a global
function `bar`, then a place `bar` for it, and then use `flet` to
locally override this `bar` a `setf` will still use the global place you
defined, AFAIK.  Same if you globally redefine the global macro/function
without redefining the place.

For this reason, in Elisp I prefer writing:

    (defun FOO (ARGS)
      (declare (gv-expander ...))
      BODY)

over

    (defun FOO (ARGS) BODY)
    (gv-define-expander FOO ...)
    
even though currently, the implementation of the former just turns it
into the latter: at least the source code makes it clear that the
expander belongs to this particular function definition, and hopefully
in some future we will correctly associate the expander to the function
and not to its symbol.


        Stefan

[toc] | [prev] | [next] | [standalone]


#33009

FromKaz Kylheku <847-115-0292@kylheku.com>
Date2019-06-03 01:13 +0000
Message-ID<20190602174604.220@kylheku.com>
In reply to#33007
On 2019-06-02, Stefan Monnier <monnier@iro.umontreal.ca> wrote:
>> I.e. given
>>
>>   (incf X)
>>
>> X gets replaced by (synthetic-place G S), nicely enough. But that is
>> immediately recognized as a macro and taken to G. So we end up with
>>
>>   (incf G) ;; the access form; S ignored
>
> That sounds positively wrong: expansion of `incf` should happen before
> we can know that (synthetic-place G S) is a Lisp expression (as opposed
> to some data used by the macro some other way) that can be
> macro-expanded, right?

Well, (incf <whatever>) knows that <whatever> is a place. It asks
for the expansion with get-setf-expansion. But note that
get-setf-expansion takes an &environment parameter: it expands the place
first: first X goes to (synthetic-place...) and then to G. THen
get-setf-expansion will get the expansion of G and return that.
incf then works with that expansion.

>> The opposite approach that I rejected does break macros in some ways.
>> Say there is some global (foo) place. Now in my code, suppose I have a
>> local macro (macrolet ((foo (...) ...))).  My code is not aware of the
>> global foo. This local foo *is* used as a place in my code: (incf
>> (foo)).  If this fails to expand my macro, but instead works with the
>> global (foo) generalized place (giving it priority), that is surprising,
>> and arguably breaks lexical scope: the macro should shadow everything.
>
> I think this is a much more general problem: the place you define is
> associated to the *symbol* foo and not to its macro definition.
> The same holds for places defined on functions: if you define a global
> function `bar`, then a place `bar` for it, and then use `flet` to
> locally override this `bar` a `setf` will still use the global place you
> defined, AFAIK.

Ah, but what if the access form of this global (bar ...j) place is simply
the (bar ...) function call? (The overwhelmingly common case).

That call will go to the local function. (incf (bar ...)) will call
the local funtion to get the value, but use the store form from the the
global setf expansion, like (bar-set ... new-value) or whatever.

The situation is half-baked between global and local.

However, with a macrolet, the user expects to hijack the (bar ...)
form completely, in all its uses. If we macrolet (bar ...) to (car ...),
we want (setf (bar ...) x) to do (setf (car ..) x) and forget the global
(bar ...) place.

This is why the CL approach is justifiable.

[toc] | [prev] | [next] | [standalone]


#33003

FromStefan Monnier <monnier@iro.umontreal.ca>
Date2019-05-31 18:04 -0400
Message-ID<jwv8suml1w0.fsf-monnier+comp.lang.lisp@gnu.org>
In reply to#32996
> Rather it's just that the user pointed out the close relationship
> between the two: you can implement a "place-alias" like your `letplace`
> using `symbol-macrolet` and you can implement `symbol-macrolet` using
> `letplace` (or so I presume, since we could do that with gv.el's
> equivalent to your `letplace`).

In a sense, `symbol-macrolet` is a kind of "call-by-name" let-binding
(except it doesn't obey lexical scoping), whereas `placelet` is a kind
of "call by reference" let-binding.

> So it made me wonder about this design space and whether maybe someone
> has found something that's "more better" than either of the two.

The CBN/CBV/CBR analogy makes me suspect such a beast doesn't exist.


        Stefan

[toc] | [prev] | [next] | [standalone]


#32934

From"Pascal J. Bourguignon" <pjb@informatimago.com>
Date2019-05-22 22:00 +0200
Message-ID<m2ftp6mf8f.fsf@despina.home>
In reply to#32931
Stefan Monnier <monnier@iro.umontreal.ca> writes:

> While discussing some extension to Elisp's `gv`, someone pointed out
> that symbol-macros basically allow you to define a symbol as an
> arbitrary "place", i.e. a kind of "place alias".
>
> Yet, it's subtly different since
>
>     (symbol-macrolet ((x (nth 5 (get-list)))) ...)
>     
> will re-execute (get-list) (and `nthcdr 5`) everytime `x` is used
> without offering any easy way to take advantage of the setf-expander
> infrastructure's ability to do those computations only once.
>
> Does anyone here know of work related to this, e.g. discussions about
> the detailed semantics of symbol-macrolet?

You're wrong.  You can see that the macroexpansion uses
get-setf-expander, which takes care of evaluating (get-list) only once:

cl-user> (defmacro m (&environment env) `',(macroexpand-1 '(incf x) env))
m
cl-user> (symbol-macrolet ((x (nth 5 (get-list)))) (m))
(let* ((#:g67596 (get-list)) (#:g67597 1) (#:g67595 (+ (nth 5 #:g67596) #:g67597))) (ccl::%setnth 5 #:g67596 #:g67595))
cl-user>


-- 
__Pascal J. Bourguignon__
http://www.informatimago.com

[toc] | [prev] | [next] | [standalone]


#32939

Froma.daniel.eliason@gmail.com
Date2019-05-23 08:10 -0700
Message-ID<e2f0ccdb-f1e8-4d1f-9572-af64c1f6120f@googlegroups.com>
In reply to#32934
On Wednesday, May 22, 2019 at 10:00:37 PM UTC+2, informatimago wrote:
> You're wrong.
> ...
> __Pascal J. Bourguignon__
> http://www.informatimago.com

While I think we value direct communication on comp.lang.lisp, sometime's it's more polite to say something like, "Actually, that turns out not to be the case."

-Daniel

[toc] | [prev] | [next] | [standalone]


#32940

FromSpiros Bousbouras <spibou@gmail.com>
Date2019-05-23 15:22 +0000
Message-ID<JUyFE.426542$v12.107225@fx29.am4>
In reply to#32939
On Thu, 23 May 2019 08:10:07 -0700 (PDT)
a.daniel.eliason@gmail.com wrote:
> On Wednesday, May 22, 2019 at 10:00:37 PM UTC+2, informatimago wrote:
> > You're wrong.
> > ...
> > __Pascal J. Bourguignon__
> > http://www.informatimago.com
> 
> While I think we value direct communication on comp.lang.lisp, sometime's
> it's more polite to say something like, "Actually, that turns out not to
> be the case."

I find both statements equally polite.

[toc] | [prev] | [next] | [standalone]


#32943

FromDimitri Fontaine <dim@tapoueh.org>
Date2019-05-23 18:02 +0200
Message-ID<m2pno9gnw0.fsf@laptop.tapoueh.org>
In reply to#32940
Spiros Bousbouras <spibou@gmail.com> writes:
> On Thu, 23 May 2019 08:10:07 -0700 (PDT)
> a.daniel.eliason@gmail.com wrote:
>> On Wednesday, May 22, 2019 at 10:00:37 PM UTC+2, informatimago wrote:
>> > You're wrong.
>> 
>> While I think we value direct communication on comp.lang.lisp, sometime's
>> it's more polite to say something like, "Actually, that turns out not to
>> be the case."
>
> I find both statements equally polite.

I sincerely don't think so. Look at the subject of the sentence. In the
first case, the OP is the target. In the second case, what he said is
the target. There's a world of difference there.

I would assume that Pascal had a hard time writing what he meant on that
one, and agree with the statement that his intervention was not polite,
or let's face it, rude.

Regards,
-- 
dim

[toc] | [prev] | [next] | [standalone]


#32944

FromSpiros Bousbouras <spibou@gmail.com>
Date2019-05-23 16:51 +0000
Message-ID<DcAFE.504630$Qy4.424505@fx04.am4>
In reply to#32943
On Thu, 23 May 2019 18:02:23 +0200
Dimitri Fontaine <dim@tapoueh.org> wrote:
> Spiros Bousbouras <spibou@gmail.com> writes:
> > On Thu, 23 May 2019 08:10:07 -0700 (PDT)
> > a.daniel.eliason@gmail.com wrote:
> >> On Wednesday, May 22, 2019 at 10:00:37 PM UTC+2, informatimago wrote:
> >> > You're wrong.
> >> 
> >> While I think we value direct communication on comp.lang.lisp, sometime's
> >> it's more polite to say something like, "Actually, that turns out not to
> >> be the case."
> >
> > I find both statements equally polite.
> 
> I sincerely don't think so. Look at the subject of the sentence. In the
> first case, the OP is the target. In the second case, what he said is
> the target. There's a world of difference there.

"You are wrong" means "This statement you made is wrong" where the statement
is clear from the context. So the target in both cases is the statement. Who
or what is the subject of each sentence is a grammatical {syntactical ?}
technicality which doesn't affect the meaning and it's the meaning that
determines who or what is the referent i.e. "target".

> I would assume that Pascal had a hard time writing what he meant on that
> one,

I find such an assumption completely without basis.

[toc] | [prev] | [next] | [standalone]


#32946

FromKaz Kylheku <847-115-0292@kylheku.com>
Date2019-05-23 17:05 +0000
Message-ID<20190523100334.725@kylheku.com>
In reply to#32940
On 2019-05-23, Spiros Bousbouras <spibou@gmail.com> wrote:
> On Thu, 23 May 2019 08:10:07 -0700 (PDT)
> a.daniel.eliason@gmail.com wrote:
>> On Wednesday, May 22, 2019 at 10:00:37 PM UTC+2, informatimago wrote:
>> > You're wrong.
>> > ...
>> > __Pascal J. Bourguignon__
>> > http://www.informatimago.com
>> 
>> While I think we value direct communication on comp.lang.lisp, sometime's
>> it's more polite to say something like, "Actually, that turns out not to
>> be the case."
>
> I find both statements equally polite.

More importantly, they are both off the mark.

Stefan was talking about multiple references to the symbol-macro, like

  (symbol-macrolet ((x (complicated-place)))
    (list x x x)) ;; place evaluated three times

Yes, a single (incf x) expression will treat the place correctly
through the symbol macro, simply because it expands into
(incf (complicated-place)).  But if we have (progn (incf x) (incf x)),
those two copies of the place know nothing about each other;
they don't share their evaluation.

-- 
TXR Programming Lanuage: http://nongnu.org/txr
Music DIY Mailing List:  http://www.kylheku.com/diy
ADA MP-1 Mailing List:   http://www.kylheku.com/mp1

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.lang.lisp


csiph-web