all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
@ 2017-05-31 23:23 Alex
  2017-05-31 23:51 ` Michael Heerdegen
  0 siblings, 1 reply; 17+ messages in thread
From: Alex @ 2017-05-31 23:23 UTC (permalink / raw)
  To: 27177

Consider the following simple cl-loop form:

(cl-loop for x in '(1 2 3)
         for y in '(a b c)
         collect (list x y))


The macroexpanded result is:

(cl-block nil
  (let*
      ((--cl-var--
        '(1 2 3))
       (x nil)
       (--cl-var--
        '(a b c))
       (y nil)
       (--cl-var-- nil))
    (while
        (and
         (consp --cl-var--)
         (progn
           (setq x
                 (car --cl-var--))
           (consp --cl-var--)))
      (setq y
            (car --cl-var--))
      (push
       (list x y)
       --cl-var--)
      (setq --cl-var--
            (cdr --cl-var--))
      (setq --cl-var--
            (cdr --cl-var--)))
    (nreverse --cl-var--)))


It's easy to verify that this expansion doesn't do the same job by
noticing that the macroexpanded form always returns nil.

Note that in Common Lisp (at least in SBCL), macroexpanding and then
evaluating the result works as expected.

This is because cl-macs.el uses make-symbol instead of gensym, like SBCL
does.

Should cl-loop and friends use cl-gensym? One possible disadvantage to
that approach is that since Emacs Lisp lacks CL's bignums, it could lead
to some issues past most-positive-fixnum.

The reason I reported this is actually because I found it difficult to
debug macroexpanded cl-loop forms when all of the uninterned symbols had
the same representation. Using cl-gensym would help with debugging.





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-05-31 23:23 bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage) Alex
@ 2017-05-31 23:51 ` Michael Heerdegen
  2017-06-01  0:29   ` Alex
  2017-06-01  0:29   ` npostavs
  0 siblings, 2 replies; 17+ messages in thread
From: Michael Heerdegen @ 2017-05-31 23:51 UTC (permalink / raw)
  To: Alex; +Cc: 27177

Alex <agrambot@gmail.com> writes:

> Consider the following simple cl-loop form:
>
> (cl-loop for x in '(1 2 3)
>          for y in '(a b c)
>          collect (list x y))
>
>
> The macroexpanded result is:
>
> (cl-block nil
>   (let*
>       ((--cl-var--
>         '(1 2 3))
>        (x nil)
>        (--cl-var--
>         '(a b c))
>        (y nil)
>        (--cl-var-- nil))
>     (while
>         (and
>          (consp --cl-var--)
>          (progn
>            (setq x
>                  (car --cl-var--))
>            (consp --cl-var--)))
>       (setq y
>             (car --cl-var--))
>       (push
>        (list x y)
>        --cl-var--)
>       (setq --cl-var--
>             (cdr --cl-var--))
>       (setq --cl-var--
>             (cdr --cl-var--)))
>     (nreverse --cl-var--)))
>
>
> It's easy to verify that this expansion doesn't do the same job by
> noticing that the macroexpanded form always returns nil.
>
> Note that in Common Lisp (at least in SBCL), macroexpanding and then
> evaluating the result works as expected.

Also in Elisp:

(eval (macroexpand '(cl-loop for x in '(1 2 3)
                        for y in '(a b c)
                        collect (list x y))))
==>
((1 a)
 (2 b)
 (3 c))

> This is because cl-macs.el uses make-symbol instead of gensym, like SBCL
> does.

Note that `make-symbol' doesn't return an interned symbol - what is
printed as "--cl-var--" above are actually different symbols.  You need
to enable `print-gensym' to make that visible when printing the
macroexpansion.  If you print with print-gensym bound to nil, you don't
get a correct printed representation.

So I think there is not a bug, unless your complaint is about human
readability or the default value of `print-gensym'.


Michael.





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-05-31 23:51 ` Michael Heerdegen
@ 2017-06-01  0:29   ` Alex
  2017-06-01  0:52     ` npostavs
  2017-06-01  1:01     ` Michael Heerdegen
  2017-06-01  0:29   ` npostavs
  1 sibling, 2 replies; 17+ messages in thread
From: Alex @ 2017-06-01  0:29 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 27177

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Also in Elisp:
>
> (eval (macroexpand '(cl-loop for x in '(1 2 3)
>                         for y in '(a b c)
>                         collect (list x y))))
> ==>
> ((1 a)
>  (2 b)
>  (3 c))

Right, sorry for not being clear. I was referring to printing the output
into a buffer and evaluating that instead of feeding it directly into
eval.

Though I was wrong that SBCL does this correctly by default (you have to
set *print-gensym* to nil).

> Note that `make-symbol' doesn't return an interned symbol - what is
> printed as "--cl-var--" above are actually different symbols.  You need
> to enable `print-gensym' to make that visible when printing the
> macroexpansion.  If you print with print-gensym bound to nil, you don't
> get a correct printed representation.

Enabling print-gensym in this case is even worse, since evaluating the
macroexpanded code yields a (void-variable #:--cl-var--) error.

> So I think there is not a bug, unless your complaint is about human
> readability or the default value of `print-gensym'.

Human readability is indeed a large part of my complaint.

Also, evaluating the macroexpanded code directly would allow for easier
debugging, since one could make small adjustments to the output to see
immediate results.






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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-05-31 23:51 ` Michael Heerdegen
  2017-06-01  0:29   ` Alex
@ 2017-06-01  0:29   ` npostavs
  1 sibling, 0 replies; 17+ messages in thread
From: npostavs @ 2017-06-01  0:29 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 27177, Alex

Michael Heerdegen <michael_heerdegen@web.de> writes:

> You need to enable `print-gensym' to make that visible when printing
> the macroexpansion.  If you print with print-gensym bound to nil, you
> don't get a correct printed representation.

You need `print-circle' as well.





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-01  0:29   ` Alex
@ 2017-06-01  0:52     ` npostavs
  2017-06-01  1:01     ` Michael Heerdegen
  1 sibling, 0 replies; 17+ messages in thread
From: npostavs @ 2017-06-01  0:52 UTC (permalink / raw)
  To: Alex; +Cc: Michael Heerdegen, 27177

Alex <agrambot@gmail.com> writes:

> Enabling print-gensym in this case is even worse, since evaluating the
> macroexpanded code yields a (void-variable #:--cl-var--) error.

Yeah, that's why you need `print-circle' too.

> Also, evaluating the macroexpanded code directly would allow for easier
> debugging, since one could make small adjustments to the output to see
> immediate results.

With print-circle, the output is debuggable in Emacs 26 (see Bug#23660).





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-01  0:29   ` Alex
  2017-06-01  0:52     ` npostavs
@ 2017-06-01  1:01     ` Michael Heerdegen
  2017-06-01  2:02       ` Alex
  1 sibling, 1 reply; 17+ messages in thread
From: Michael Heerdegen @ 2017-06-01  1:01 UTC (permalink / raw)
  To: Alex; +Cc: 27177

Alex <agrambot@gmail.com> writes:

> > Note that `make-symbol' doesn't return an interned symbol - what is
> > printed as "--cl-var--" above are actually different symbols.  You need
> > to enable `print-gensym' to make that visible when printing the
> > macroexpansion.  If you print with print-gensym bound to nil, you don't
> > get a correct printed representation.
>
> Enabling print-gensym in this case is even worse, since evaluating the
> macroexpanded code yields a (void-variable #:--cl-var--) error.

Oh, you need to bind `print-circle' as well, thanks npostavs.

> > So I think there is not a bug, unless your complaint is about human
> > readability or the default value of `print-gensym'.
>
> Human readability is indeed a large part of my complaint.

> Also, evaluating the macroexpanded code directly would allow for easier
> debugging, since one could make small adjustments to the output to see
> immediate results.

With print-circle and print-gensym bound, I think the result does not
really read worse than how it would look like with with `cl-gensym'
generated code.


Michael.





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-01  1:01     ` Michael Heerdegen
@ 2017-06-01  2:02       ` Alex
  2017-06-02  3:27         ` Michael Heerdegen
  0 siblings, 1 reply; 17+ messages in thread
From: Alex @ 2017-06-01  2:02 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 27177, npostavs

Michael Heerdegen <michael_heerdegen@web.de> writes:

> With print-circle and print-gensym bound, I think the result does not
> really read worse than how it would look like with with `cl-gensym'
> generated code.

That's much better, though I still think it could/should be better. For
example, if you have multiple uninterned symbols with different
symbol-names, they're all referenced by #number, and use the same
counter.

It also seems to make the output uglier as well. Consider:

(macroexpand '(cl-loop for x in '(1 2 3)
                       for y in '(a b c)
                       repeat 10
                       repeat 20
                       collect (list x y)))

Note the expressions using #5#. I suppose the 0 is being shared.

It would also be nice if instead of many --cl-var-- variables,
particular clauses would result in different symbol-names. For instance,
if the `repeat' clause made a symbol called --cl-repeat--. This would
further help readability.

Also, using gensym could help 3rd-party packages. I usually use
macrostep to expand macros and the value of print-circle has no effect
on its expansions. macrostep individually prints out each uninterned
symbol using prin1; can this approach be easily modified to get the same
result as macroexpand?

PS: The first line of the documentation of print-circle only mentions
that it affects recursive structures. Perhaps it should mention the
"shared substructures" part in the first line for emphasis?





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-01  2:02       ` Alex
@ 2017-06-02  3:27         ` Michael Heerdegen
  2017-06-02  4:42           ` Alex
  0 siblings, 1 reply; 17+ messages in thread
From: Michael Heerdegen @ 2017-06-02  3:27 UTC (permalink / raw)
  To: Alex; +Cc: 27177, npostavs

Alex <agrambot@gmail.com> writes:

> That's much better, though I still think it could/should be better. For
> example, if you have multiple uninterned symbols with different
> symbol-names, they're all referenced by #number, and use the same
> counter.
>
> It also seems to make the output uglier as well. Consider:
>
> (macroexpand '(cl-loop for x in '(1 2 3)
>                        for y in '(a b c)
>                        repeat 10
>                        repeat 20
>                        collect (list x y)))
>
> Note the expressions using #5#. I suppose the 0 is being shared.

Yes, or at least, a cons containing the 0.  That's how things look like
with print-circle enabled.

> It would also be nice if instead of many --cl-var-- variables,
> particular clauses would result in different symbol-names. For instance,
> if the `repeat' clause made a symbol called --cl-repeat--. This would
> further help readability.

I'm not sure if that is doable without rewriting the implementation,
since the macro expansion is automatically written code.

> Also, using gensym could help 3rd-party packages. I usually use
> macrostep to expand macros and the value of print-circle has no effect
> on its expansions. macrostep individually prints out each uninterned
> symbol using prin1; can this approach be easily modified to get the same
> result as macroexpand?

AFAICT `print-circle' and `print-gensym' also control how `prin1'
prints.


Note that when we changed the code to use `cl-gensym', we would not have
a really clean solution for the readability problem: if you print with
p-gensym and p-circle on, it would not look much different than now.  If
you print with those flags off, you (still) print to different (not
equivalent) code: when you read (evaluate) it, all uninterned symbols
would be replaced with interned symbols.  Though, with numbered symbol
names, you will be probably be lucky in most cases that the difference
doesn't matter.

But I see your point: the readability is a real problem.  Maybe we could
instead improve how things are printed?  Unfortunately that lies beyond
my knowledge.


> PS: The first line of the documentation of print-circle only mentions
> that it affects recursive structures. Perhaps it should mention the
> "shared substructures" part in the first line for emphasis?

I agree but somewhat hesitate because of the variable's name, which is
even more a source of confusion.


Michael.





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-02  3:27         ` Michael Heerdegen
@ 2017-06-02  4:42           ` Alex
  2017-06-02 23:09             ` Michael Heerdegen
                               ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Alex @ 2017-06-02  4:42 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 27177, monnier, npostavs

Michael Heerdegen <michael_heerdegen@web.de> writes:

>> It would also be nice if instead of many --cl-var-- variables,
>> particular clauses would result in different symbol-names. For instance,
>> if the `repeat' clause made a symbol called --cl-repeat--. This would
>> further help readability.
>
> I'm not sure if that is doable without rewriting the implementation,
> since the macro expansion is automatically written code.

I'm not sure what you mean. Since they're uninterned symbols their
symbol-names shouldn't matter unless a procedure calls #'symbol-name on
them. In cl-macs.el I don't see any examples of that.

It appears that the code sometimes does use different names already in a
couple places. For example, see --cl-vec-- and --cl-idx-- at about line
1294 in cl-macs.el.

Actually, looking at the git-blame for those lines, it looks like Stefan
switched from gensym to make-symbol all the way back in e542ea4bed!

Stefan, why did you make the switch? Using cl-gensym would help a ton
with readability of cl-loop's macroexpansion.

>> Also, using gensym could help 3rd-party packages. I usually use
>> macrostep to expand macros and the value of print-circle has no effect
>> on its expansions. macrostep individually prints out each uninterned
>> symbol using prin1; can this approach be easily modified to get the same
>> result as macroexpand?
>
> AFAICT `print-circle' and `print-gensym' also control how `prin1'
> prints.

Does print-circle? Consider:

(prin1 `(cons ,(make-symbol "hello")
	      ,(make-symbol "hello")))

print-gensym certainly makes a difference in the output, but
print-circle doesn't seem to.

However, I don't know how prin1 would keep track of the uninterned
symbols across many different procedure calls, which it would need to do
for it to know what is being shared.

> Note that when we changed the code to use `cl-gensym', we would not have
> a really clean solution for the readability problem: if you print with
> p-gensym and p-circle on, it would not look much different than now.  If
> you print with those flags off, you (still) print to different (not
> equivalent) code: when you read (evaluate) it, all uninterned symbols
> would be replaced with interned symbols.  Though, with numbered symbol
> names, you will be probably be lucky in most cases that the difference
> doesn't matter.

In the case with both flags off and with make-symbol calls changed to
cl-gensym, I got:

(cl-block nil
  (let*
      ((--cl-var--248
        '(1 2 3))
       (x nil)
       (--cl-var--249
        '(a b c))
       (y nil)
       (--cl-var--250 nil))
    (while
        (and
         (consp --cl-var--248)
         (progn
           (setq x
                 (car --cl-var--248))
           (consp --cl-var--249)))
      (setq y
            (car --cl-var--249))
      (push
       (list x y)
       --cl-var--250)
      (setq --cl-var--248
            (cdr --cl-var--248))
      (setq --cl-var--249
            (cdr --cl-var--249)))
    (nreverse --cl-var--250)))

Which is equivalent code to the original cl-loop. I also believe it's
much more readable than the current macroexpansion as you can actually
differentiate between the different --cl-var-- variables. Using
different symbol names as discussed above would help a lot as well, but
I still think this part is important as well.

> But I see your point: the readability is a real problem.  Maybe we could
> instead improve how things are printed?  Unfortunately that lies beyond
> my knowledge.

I'm not sure what you mean, but improvement is certainly welcome.

>> PS: The first line of the documentation of print-circle only mentions
>> that it affects recursive structures. Perhaps it should mention the
>> "shared substructures" part in the first line for emphasis?
>
> I agree but somewhat hesitate because of the variable's name, which is
> even more a source of confusion.

Right, I was a bit confused as well, but according to the Hyperspec
*print-circle* also detects sharing in objects in Common Lisp as well.





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-02  4:42           ` Alex
@ 2017-06-02 23:09             ` Michael Heerdegen
  2017-06-02 23:17             ` npostavs
  2017-06-03  2:33             ` Stefan Monnier
  2 siblings, 0 replies; 17+ messages in thread
From: Michael Heerdegen @ 2017-06-02 23:09 UTC (permalink / raw)
  To: Alex; +Cc: 27177

Alex <agrambot@gmail.com> writes:

> > AFAICT `print-circle' and `print-gensym' also control how `prin1'
> > prints.
>
> Does print-circle? Consider:
>
> (prin1 `(cons ,(make-symbol "hello")
>               ,(make-symbol "hello")))
>
> print-gensym certainly makes a difference in the output, but
> print-circle doesn't seem to.

There are no shared parts in your example expression, so it doesn't make
a difference in this particular case.

> However, I don't know how prin1 would keep track of the uninterned
> symbols across many different procedure calls, which it would need to do
> for it to know what is being shared.

It doesn't AFAICT.


Michael.





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-02  4:42           ` Alex
  2017-06-02 23:09             ` Michael Heerdegen
@ 2017-06-02 23:17             ` npostavs
  2017-06-02 23:46               ` Alex
  2017-06-03  2:33             ` Stefan Monnier
  2 siblings, 1 reply; 17+ messages in thread
From: npostavs @ 2017-06-02 23:17 UTC (permalink / raw)
  To: Alex; +Cc: Michael Heerdegen, 27177, monnier

Alex <agrambot@gmail.com> writes:

>> AFAICT `print-circle' and `print-gensym' also control how `prin1'
>> prints.
>
> Does print-circle? Consider:
>
> (prin1 `(cons ,(make-symbol "hello")
> 	      ,(make-symbol "hello")))
>
> print-gensym certainly makes a difference in the output, but
> print-circle doesn't seem to.

You're producing 2 different symbols, try

    (let ((sym (make-symbol "hello")))
      (prin1 `(cons ,sym ,sym)))

> However, I don't know how prin1 would keep track of the uninterned
> symbols across many different procedure calls, which it would need to do
> for it to know what is being shared.

It looks like `print-continuous-numbering' and `print-number-table'
might be relevant.





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-02 23:17             ` npostavs
@ 2017-06-02 23:46               ` Alex
  0 siblings, 0 replies; 17+ messages in thread
From: Alex @ 2017-06-02 23:46 UTC (permalink / raw)
  To: npostavs; +Cc: Michael Heerdegen, 27177, monnier

npostavs@users.sourceforge.net writes:

> Alex <agrambot@gmail.com> writes:
>
>>> AFAICT `print-circle' and `print-gensym' also control how `prin1'
>>> prints.
>>
>> Does print-circle? Consider:
>>
>> (prin1 `(cons ,(make-symbol "hello")
>> 	      ,(make-symbol "hello")))
>>
>> print-gensym certainly makes a difference in the output, but
>> print-circle doesn't seem to.
>
> You're producing 2 different symbols, try
>
>     (let ((sym (make-symbol "hello")))
>       (prin1 `(cons ,sym ,sym)))

Right, sorry for the bad example. I don't know what I was thinking.

>> However, I don't know how prin1 would keep track of the uninterned
>> symbols across many different procedure calls, which it would need to do
>> for it to know what is being shared.
>
> It looks like `print-continuous-numbering' and `print-number-table'
> might be relevant.

That does look like an option, thanks. I'd still like for cl-macs.el to
produce readable output without all of these extra print-* variables
set, but if it's infeasible to do that, then I'll look into using those.





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-02  4:42           ` Alex
  2017-06-02 23:09             ` Michael Heerdegen
  2017-06-02 23:17             ` npostavs
@ 2017-06-03  2:33             ` Stefan Monnier
  2017-06-04  0:24               ` Alex
  2020-08-24 14:53               ` Lars Ingebrigtsen
  2 siblings, 2 replies; 17+ messages in thread
From: Stefan Monnier @ 2017-06-03  2:33 UTC (permalink / raw)
  To: Alex; +Cc: Michael Heerdegen, 27177, npostavs

> Stefan, why did you make the switch? Using cl-gensym would help a ton
> with readability of cl-loop's macroexpansion.

I don't consider human-readability of the result to be something that
a macro should have to pay attention to.


        Stefan





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-03  2:33             ` Stefan Monnier
@ 2017-06-04  0:24               ` Alex
  2017-06-06  4:20                 ` Stefan Monnier
  2020-08-24 14:53               ` Lars Ingebrigtsen
  1 sibling, 1 reply; 17+ messages in thread
From: Alex @ 2017-06-04  0:24 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Michael Heerdegen, 27177, npostavs

Stefan Monnier <monnier@IRO.UMontreal.CA> writes:

>> Stefan, why did you make the switch? Using cl-gensym would help a ton
>> with readability of cl-loop's macroexpansion.
>
> I don't consider human-readability of the result to be something that
> a macro should have to pay attention to.

I can understand that when talking about smaller macros, but loop is a
fairly complex macro, so I believe human-readability is a desirable
trait to have if feasible. It would help with debugging and overall
understanding of cl-loop.

I've browsed around for a few common loop implementations and they all
use gensym (CCL uses gentemp) and descriptive naming:

SBCL: https://github.com/sbcl/sbcl/blob/master/src/code/loop.lisp

CLISP: https://sourceforge.net/p/clisp/clisp/ci/default/tree/src/loop.lisp

CCL: https://github.com/Clozure/ccl/blob/master/library/loop.lisp

ABCL (admittedly they adapted it from SBCL):
http://abcl.org/trac/browser/trunk/abcl/src/org/armedbear/lisp/loop.lisp

I also found a CHICKEN Scheme egg for CL's loop, and it uses gensym (but
generic names, unfortunately).

If there's a good reason to not use gensym, then that's fine, but if the
problem is easy enough to work around (perhaps per-expansion counter so
that it will never realistically hit most-positive-fixnum), then I think
cl-loop should use it.





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-04  0:24               ` Alex
@ 2017-06-06  4:20                 ` Stefan Monnier
  2017-06-06 20:31                   ` Alex
  0 siblings, 1 reply; 17+ messages in thread
From: Stefan Monnier @ 2017-06-06  4:20 UTC (permalink / raw)
  To: Alex; +Cc: Michael Heerdegen, 27177, npostavs

> I've browsed around for a few common loop implementations and they all
> use gensym (CCL uses gentemp) and descriptive naming:

gensym is the indeed what is commonly used in Common-Lisp, whereas
make-symbol is what is commonly used in ELisp.

> I also found a CHICKEN Scheme egg for CL's loop, and it uses gensym (but
> generic names, unfortunately).

Does Scheme have make-symbol or something equivalent?

> If there's a good reason to not use gensym, then that's fine, but if the
> problem is easy enough to work around (perhaps per-expansion counter so
> that it will never realistically hit most-positive-fixnum), then I think
> cl-loop should use it.

I'd prefer to solve it in the printer, but that's just my opinion.
FWIW, I've found print-gensym to be sufficient.


        Stefan





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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-06  4:20                 ` Stefan Monnier
@ 2017-06-06 20:31                   ` Alex
  0 siblings, 0 replies; 17+ messages in thread
From: Alex @ 2017-06-06 20:31 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Michael Heerdegen, 27177, npostavs

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

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

>> I've browsed around for a few common loop implementations and they all
>> use gensym (CCL uses gentemp) and descriptive naming:
>
> gensym is the indeed what is commonly used in Common-Lisp, whereas
> make-symbol is what is commonly used in ELisp.

Right, but it seems natural that the CL compatibility macros would use
gensym since CL does it that way.

>> I also found a CHICKEN Scheme egg for CL's loop, and it uses gensym (but
>> generic names, unfortunately).
>
> Does Scheme have make-symbol or something equivalent?

The RnRS standards don't specify uninterned symbols, but they mention
that some implementations have them. A quick check shows that Guile has
make-symbol, and CHICKEN and Racket have equivalents to make-symbol
(string->uninterned-symbol). All 3 have gensym.

>> If there's a good reason to not use gensym, then that's fine, but if the
>> problem is easy enough to work around (perhaps per-expansion counter so
>> that it will never realistically hit most-positive-fixnum), then I think
>> cl-loop should use it.
>
> I'd prefer to solve it in the printer, but that's just my opinion.
> FWIW, I've found print-gensym to be sufficient.

How do you want to solve it in the printer? One way I thought of was to
keep a counter similar to cl--gensym-counter, and make a hash table of
uninterned symbols with values being the prefix to concat to the end of
the print name of the symbol. At this point, though, why not port gensym
to ELisp proper and encourage use of gensym? I don't see the use for
extra complexity if it's not needed.

A seemingly simple option, as mentioned before, is to bind
cl--gensym-counter to 0 at the start of cl-loop. That means cl-loop
won't increase the global counter, and it fixes the readability of the
macro. I attached a sample diff below, which seems to work fine.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: gensym.diff --]
[-- Type: text/x-diff, Size: 11329 bytes --]

diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index db1518ce61..5b711a2a79 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -544,7 +544,7 @@ cl--do-arglist
 	  (laterarg nil) (exactarg nil) minarg)
       (or num (setq num 0))
       (setq restarg (if (listp (cadr restarg))
-                        (make-symbol "--cl-rest--")
+                        (cl-gensym "--cl-rest--")
                       (cadr restarg)))
       (push (list restarg expr) cl--bind-lets)
       (if (eq (car args) '&whole)
@@ -617,7 +617,7 @@ cl--do-arglist
                    (look `(plist-member ,restarg ',karg)))
 	      (and def cl--bind-enquote (setq def `',def))
 	      (if (cddr arg)
-		  (let* ((temp (or (nth 2 arg) (make-symbol "--cl-var--")))
+		  (let* ((temp (or (nth 2 arg) (cl-gensym "--cl-var--")))
 			 (val `(car (cdr ,temp))))
 		    (cl--do-arglist temp look)
 		    (cl--do-arglist varg
@@ -636,7 +636,7 @@ cl--do-arglist
       (setq keys (nreverse keys))
       (or (and (eq (car args) '&allow-other-keys) (pop args))
 	  (null keys) (= safety 0)
-	  (let* ((var (make-symbol "--cl-keys--"))
+	  (let* ((var (cl-gensym "--cl-keys--"))
 		 (allow '(:allow-other-keys))
 		 (check `(while ,var
                            (cond
@@ -936,7 +936,8 @@ cl-loop
 	  (cl--loop-accum-var nil)	(cl--loop-accum-vars nil)
 	  (cl--loop-initially nil)	(cl--loop-finally nil)
 	  (cl--loop-iterator-function nil) (cl--loop-first-flag nil)
-          (cl--loop-symbol-macs nil))
+          (cl--loop-symbol-macs nil)
+          (cl--gensym-counter 0))
       ;; Here is more or less how those dynbind vars are used after looping
       ;; over cl--parse-loop-clause:
       ;;
@@ -1225,9 +1226,9 @@ cl--parse-loop-clause
 		       (step (and (eq (car cl--loop-args) 'by)
                                   (cl--pop2 cl--loop-args)))
 		       (end-var (and (not (macroexp-const-p end))
-				     (make-symbol "--cl-var--")))
+				     (cl-gensym "--cl-var--")))
 		       (step-var (and (not (macroexp-const-p step))
-				      (make-symbol "--cl-var--"))))
+				      (cl-gensym "--cl-var--"))))
 		  (and step (numberp step) (<= step 0)
 		       (error "Loop `by' value is not positive: %s" step))
 		  (push (list var (or start 0)) loop-for-bindings)
@@ -1246,7 +1247,7 @@ cl--parse-loop-clause
 	       ((memq word '(in in-ref on))
 		(let* ((on (eq word 'on))
 		       (temp (if (and on (symbolp var))
-				 var (make-symbol "--cl-var--"))))
+				 var (cl-gensym "--cl-var--"))))
 		  (push (list temp (pop cl--loop-args)) loop-for-bindings)
 		  (push `(consp ,temp) cl--loop-body)
 		  (if (eq word 'in-ref)
@@ -1278,7 +1279,7 @@ cl--parse-loop-clause
 			(push `(,var
 				(if ,(or cl--loop-first-flag
 					 (setq cl--loop-first-flag
-					       (make-symbol "--cl-var--")))
+					       (cl-gensym "--cl-var--")))
 				    ,start ,var))
 			      loop-for-sets)
 			(push (list var then) loop-for-steps))
@@ -1286,13 +1287,13 @@ cl--parse-loop-clause
 				(if (eq start then) start
 				  `(if ,(or cl--loop-first-flag
 					    (setq cl--loop-first-flag
-						  (make-symbol "--cl-var--")))
+						  (cl-gensym "--cl-var--")))
 				       ,start ,then)))
 			  loop-for-sets))))
 
 	       ((memq word '(across across-ref))
-		(let ((temp-vec (make-symbol "--cl-vec--"))
-		      (temp-idx (make-symbol "--cl-idx--")))
+		(let ((temp-vec (cl-gensym "--cl-vec--"))
+		      (temp-idx (cl-gensym "--cl-idx--")))
 		  (push (list temp-vec (pop cl--loop-args)) loop-for-bindings)
 		  (push (list temp-idx -1) loop-for-bindings)
 		  (push `(< (setq ,temp-idx (1+ ,temp-idx))
@@ -1310,18 +1311,18 @@ cl--parse-loop-clause
 			       (and (not (memq (car cl--loop-args) '(in of)))
 				    (error "Expected `of'"))))
 		      (seq (cl--pop2 cl--loop-args))
-		      (temp-seq (make-symbol "--cl-seq--"))
+		      (temp-seq (cl-gensym "--cl-seq--"))
 		      (temp-idx
                        (if (eq (car cl--loop-args) 'using)
                            (if (and (= (length (cadr cl--loop-args)) 2)
                                     (eq (cl-caadr cl--loop-args) 'index))
                                (cadr (cl--pop2 cl--loop-args))
                              (error "Bad `using' clause"))
-                         (make-symbol "--cl-idx--"))))
+                         (cl-gensym "--cl-idx--"))))
 		  (push (list temp-seq seq) loop-for-bindings)
 		  (push (list temp-idx 0) loop-for-bindings)
 		  (if ref
-		      (let ((temp-len (make-symbol "--cl-len--")))
+		      (let ((temp-len (cl-gensym "--cl-len--")))
 			(push (list temp-len `(length ,temp-seq))
 			      loop-for-bindings)
 			(push (list var `(elt ,temp-seq ,temp-idx))
@@ -1350,7 +1351,7 @@ cl--parse-loop-clause
                                      (not (eq (cl-caadr cl--loop-args) word)))
                                 (cadr (cl--pop2 cl--loop-args))
                               (error "Bad `using' clause"))
-                          (make-symbol "--cl-var--"))))
+                          (cl-gensym "--cl-var--"))))
 		  (if (memq word '(hash-value hash-values))
 		      (setq var (prog1 other (setq other var))))
 		  (cl--loop-set-iterator-function
@@ -1377,14 +1378,14 @@ cl--parse-loop-clause
 		  (cl--loop-set-iterator-function
                    'overlays (lambda (body)
                                `(cl--map-overlays
-                                 (lambda (,var ,(make-symbol "--cl-var--"))
+                                 (lambda (,var ,(cl-gensym "--cl-var--"))
                                    (progn . ,body) nil)
                                  ,buf ,from ,to)))))
 
 	       ((memq word '(interval intervals))
 		(let ((buf nil) (prop nil) (from nil) (to nil)
-		      (var1 (make-symbol "--cl-var1--"))
-		      (var2 (make-symbol "--cl-var2--")))
+		      (var1 (cl-gensym "--cl-var1--"))
+		      (var2 (cl-gensym "--cl-var2--")))
 		  (while (memq (car cl--loop-args) '(in of property from to))
 		    (cond ((eq (car cl--loop-args) 'from)
                            (setq from (cl--pop2 cl--loop-args)))
@@ -1413,7 +1414,7 @@ cl--parse-loop-clause
                                     (not (eq (cl-caadr cl--loop-args) word)))
                                (cadr (cl--pop2 cl--loop-args))
                              (error "Bad `using' clause"))
-                         (make-symbol "--cl-var--"))))
+                         (cl-gensym "--cl-var--"))))
 		  (if (memq word '(key-binding key-bindings))
 		      (setq var (prog1 other (setq other var))))
 		  (cl--loop-set-iterator-function
@@ -1423,7 +1424,7 @@ cl--parse-loop-clause
                              (lambda (,var ,other) . ,body) ,cl-map)))))
 
 	       ((memq word '(frame frames screen screens))
-		(let ((temp (make-symbol "--cl-var--")))
+		(let ((temp (cl-gensym "--cl-var--")))
 		  (push (list var  '(selected-frame))
 			loop-for-bindings)
 		  (push (list temp nil) loop-for-bindings)
@@ -1436,8 +1437,8 @@ cl--parse-loop-clause
 	       ((memq word '(window windows))
 		(let ((scr (and (memq (car cl--loop-args) '(in of))
                                 (cl--pop2 cl--loop-args)))
-		      (temp (make-symbol "--cl-var--"))
-		      (minip (make-symbol "--cl-minip--")))
+		      (temp (cl-gensym "--cl-var--"))
+		      (minip (cl-gensym "--cl-minip--")))
 		  (push (list var (if scr
 				      `(frame-selected-window ,scr)
 				    '(selected-window)))
@@ -1481,7 +1482,7 @@ cl--parse-loop-clause
 		  cl--loop-steps))))
 
      ((eq word 'repeat)
-      (let ((temp (make-symbol "--cl-var--")))
+      (let ((temp (cl-gensym "--cl-var--")))
 	(push (list (list temp (pop cl--loop-args))) cl--loop-bindings)
 	(push `(>= (setq ,temp (1- ,temp)) 0) cl--loop-body)))
 
@@ -1560,22 +1561,22 @@ cl--parse-loop-clause
 
      ((eq word 'always)
       (or cl--loop-finish-flag
-          (setq cl--loop-finish-flag (make-symbol "--cl-flag--")))
+          (setq cl--loop-finish-flag (cl-gensym "--cl-flag--")))
       (push `(setq ,cl--loop-finish-flag ,(pop cl--loop-args)) cl--loop-body)
       (setq cl--loop-result t))
 
      ((eq word 'never)
       (or cl--loop-finish-flag
-          (setq cl--loop-finish-flag (make-symbol "--cl-flag--")))
+          (setq cl--loop-finish-flag (cl-gensym "--cl-flag--")))
       (push `(setq ,cl--loop-finish-flag (not ,(pop cl--loop-args)))
 	    cl--loop-body)
       (setq cl--loop-result t))
 
      ((eq word 'thereis)
       (or cl--loop-finish-flag
-          (setq cl--loop-finish-flag (make-symbol "--cl-flag--")))
+          (setq cl--loop-finish-flag (cl-gensym "--cl-flag--")))
       (or cl--loop-result-var
-          (setq cl--loop-result-var (make-symbol "--cl-var--")))
+          (setq cl--loop-result-var (cl-gensym "--cl-var--")))
       (push `(setq ,cl--loop-finish-flag
                    (not (setq ,cl--loop-result-var ,(pop cl--loop-args))))
 	    cl--loop-body))
@@ -1607,9 +1608,9 @@ cl--parse-loop-clause
 
      ((eq word 'return)
       (or cl--loop-finish-flag
-          (setq cl--loop-finish-flag (make-symbol "--cl-var--")))
+          (setq cl--loop-finish-flag (cl-gensym "--cl-var--")))
       (or cl--loop-result-var
-          (setq cl--loop-result-var (make-symbol "--cl-var--")))
+          (setq cl--loop-result-var (cl-gensym "--cl-var--")))
       (push `(setq ,cl--loop-result-var ,(pop cl--loop-args)
                    ,cl--loop-finish-flag nil)
             cl--loop-body))
@@ -1640,7 +1641,7 @@ cl--loop-let
           (setq par nil)
           (dolist (spec specs)
             (or (macroexp-const-p (cadr spec))
-                (let ((temp (make-symbol "--cl-var--")))
+                (let ((temp (cl-gensym "--cl-var--")))
                   (push (list temp (cadr spec)) temps)
                   (setcar (cdr spec) temp)))))))
     (while specs
@@ -1657,7 +1658,7 @@ cl--loop-let
                           (and (eq body 'setq) (cl--unused-var-p temp)))
                   ;; Prefer a fresh uninterned symbol over "_to", to avoid
                   ;; warnings that we set an unused variable.
-                  (setq temp (make-symbol "--cl-var--"))
+                  (setq temp (cl-gensym "--cl-var--"))
                   ;; Make sure this temp variable is locally declared.
                   (when (eq body 'setq)
                     (push (list (list temp)) cl--loop-bindings)))
@@ -1685,7 +1686,7 @@ cl--loop-handle-accum
     (or cl--loop-accum-var
 	(progn
 	  (push (list (list
-                       (setq cl--loop-accum-var (make-symbol "--cl-var--"))
+                       (setq cl--loop-accum-var (cl-gensym "--cl-var--"))
                        def))
                 cl--loop-bindings)
 	  (setq cl--loop-result (if func (list func cl--loop-accum-var)
diff --git a/lisp/ffap.el b/lisp/ffap.el
index 87531110b8..3eee3f3878 100644
--- a/lisp/ffap.el
+++ b/lisp/ffap.el
@@ -1326,7 +1326,8 @@ ffap-file-at-point
 	 ;; If it contains a colon, get rid of it (and return if exists)
 	 ((and (string-match path-separator name)
 	       (setq name (ffap-string-at-point 'nocolon))
-	       (ffap-file-exists-string name)))
+               (not (string-empty-p name))
+               (ffap-file-exists-string name)))
 	 ;; File does not exist, try the alist:
 	 ((let ((alist ffap-alist) tem try case-fold-search)
 	    (while (and alist (not try))

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

* bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)
  2017-06-03  2:33             ` Stefan Monnier
  2017-06-04  0:24               ` Alex
@ 2020-08-24 14:53               ` Lars Ingebrigtsen
  1 sibling, 0 replies; 17+ messages in thread
From: Lars Ingebrigtsen @ 2020-08-24 14:53 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Michael Heerdegen, 27177, Alex, npostavs

Stefan Monnier <monnier@IRO.UMontreal.CA> writes:

>> Stefan, why did you make the switch? Using cl-gensym would help a ton
>> with readability of cl-loop's macroexpansion.
>
> I don't consider human-readability of the result to be something that
> a macro should have to pay attention to.

There didn't seem to be much enthusiasm for changing the code generation
here, so I'm closing this bug report.  If there is something more to be
done here, please respond to the debbugs address and we'll reopen.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no





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

end of thread, other threads:[~2020-08-24 14:53 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-05-31 23:23 bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage) Alex
2017-05-31 23:51 ` Michael Heerdegen
2017-06-01  0:29   ` Alex
2017-06-01  0:52     ` npostavs
2017-06-01  1:01     ` Michael Heerdegen
2017-06-01  2:02       ` Alex
2017-06-02  3:27         ` Michael Heerdegen
2017-06-02  4:42           ` Alex
2017-06-02 23:09             ` Michael Heerdegen
2017-06-02 23:17             ` npostavs
2017-06-02 23:46               ` Alex
2017-06-03  2:33             ` Stefan Monnier
2017-06-04  0:24               ` Alex
2017-06-06  4:20                 ` Stefan Monnier
2017-06-06 20:31                   ` Alex
2020-08-24 14:53               ` Lars Ingebrigtsen
2017-06-01  0:29   ` npostavs

Code repositories for project(s) associated with this external index

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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.