Subject: bug#27177: 26.0.50: Macroexpanding cl-loop and friends (make-symbol usage)

From: Alex <[email-masked]>
Date: Thu, 01 Jun 2017 22:42:45 -0600

Michael Heerdegen <[email-masked]> 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.