Subject: Re: bug#55645: src/print.c; print_object changes make it impossible to compare elisp code across versions

From: Tom Gillespie <[email-masked]>
Date: Mon, 6 Jun 2022 16:30:22 -0700

Hi Eli,
   Replies in line. Best!
Tom

> You say in your OP that there's no way to work around the issue, but I
> wonder if this is really so.  It seems to me that by adding a
> backslash before '.' and '?', before computing the hash, would be one
> workaround?

Unfortunately I don't think there is an easy way to do such a thing
from elisp. The primary issue with such a workaround is that while
it is safe to do that inside strings and symbols, you have to know
the type of the object that is being printed, otherwise floats and
characters will be inadvertently converted to symbols.

As far as I can tell the only place to make such a change is inside
the c implementation of prin1 or some other c function, thus my request
for a dynamic variable that would preserve the old behavior by allowing
me to reach into the c runtime.

> Frankly, I'm amazed that someone could have such faith into
> immutability of Emacs Lisp.  Why would you assume that the above
> produces the same literal string across versions, especially given
> everything that happens lately in Emacs, including changes in the
> byte-compiler, the introduction of native-compilation, etc.

I wouldn't call it faith so much as testing, and perhaps an assumption
that there wasn't any reason to change something so fundamental
that affected so few people and had been stable for so long.

Also, it does not seem such an unfounded assumption because for code
the prin1 representation (or at least the pp-buffer representation) must
produce valid elisp that can be read, and the reader is extremely stable.
In particular the representation also ideally needs to be read by older
versions of emacs because some libraries would like to be able to
support multiple versions of emacs with a single.el file.

As I note below the particular relaxation in the behavior of the reader
was made long ago, the change in the prin1 behavior only affects very
old versions of emacs, though emacs-18 is installable and usable on
gentoo, I have no idea whether anyone actually shares code between
emacs-18 and emacs-29, so that is likely a moot point.

> If someone would show me a design that is based on this, I'd tell them
> they place their bet on a dubious horse, and suggest to find a better
> design.  To me, this is taking some Emacs feature that just happened
> to be stable, and assuming it will forever be stable.  There's no
> reason and no real basis for such assumptions.

As mentioned above, there is a strong basis for such assumptions,
which is that prin1 or at least pp-buffer must produce valid elisp code
that can be read.

Thus the stability of prin1 is intrinsically tied to what the reader
will accept. So the change that we are discussing here is due to
the fact that there was a change in the reader to relax what it would
accept with regard to '.' and '?' in symbols.

>
> Thus, I agree with Lars that it is strange to hear that prin1 is used
> as something that's supposed to produce a canonical representation of
> Lisp code; it's definitely isn't its purpose.

Am I wrong in my assumption about pp-buffer and read? I mean, yes
in principle prin1 could start escaping every single character in every
symbol since it will ultimately read in the same way, but that seems
somewhat unlikely.

> Anyway, one way forward is to add a new API specifically for that
> purpose, and then guarantee that the output of that new API will be
> stable.  This will also take care of the issue with the design that
> relies on prin1.

This seems like the "right" solution long term. It allows us to skip
the aesthetics discussion going on at the moment, and it provides
an explicitly stable representation for readable forms, allowing prin1
to be free of any such constraints (complaints?) in the future.
It would be possible to use fboundp to check whether the new stable
prin1 was present and use that going forward, if it is not found then
prin1 itself should be fine through 28. This seems preferable to having
an implicit subset of readable objects that are mostly stable in prin1
but can sometimes change to match current reader behavior.