Subject: Re: bug#68568: transient.el interns potentially enormous symbols as commands

From: Jonas Bernoulli <[email-masked]>
Date: Sat, 27 Jan 2024 19:18:22 +0100

Eli Zaretskii <[email-masked]> writes:

> Ping!
>
> Jonas, any comments or suggestions?

We discussed at https://github.com/magit/transient/pull/273.

Summary: It would probably be possible to avoid interning these symbols.
I did some experimentation to come to that provisional assessment.  But
Transient has been using interned from the beginning, and as a result,
many parts of Transient assume that interned symbols are being used.
Using symbols made this aspect of Transient much simpler.  This allowed
me to focus on gradually improve other aspects, which are more
complicated by necessity.  If I had not used symbols, this would have
slowed down development.  Switching to not using symbols for some
commands, would mean many things would have to be adjusted to handle
both cases.  This would undoubtedly lead to bugs and slow down
development.  I would rather work on the parts of Transient that IMO
need improvement.

IMO using commands that are fbound to interned symbols has one and
only one drawback: they are offered as completion candidates by
execute-extended-command.

I have addressed this by setting read-extended-command-predicate to a
function that hides "transient" commands that should be hidden and
nothing else. The option is only set if, and only if, it is not already
set to something other than nil.  If the user customizes the option and
pick one of the predicates offered by Emacs, then that predicate also
hides Transient's "anonymous" commands. [There was a bug, I just fixed
that.]

IMO the result of this approach is that the only drawback of interning
the symbols is gone; without me having to trade in that one drawback for
many avoidable complications, as I would have to if I followed the
request to not "needlessly" intern the "anonymous" symbols.

An additional benefit of using read-extended-command-predicate is that
it allows all of Transient's commands that should be hidden, to be
hidden.  Even if I did stop using interned symbols for the "anonymous"
commands, which are inline when a transient menu is defined, there would
still be other commands that are defined non-anonymously, but never the
less should be hidden.  (E.g., commands that toggle arguments and are
defined using transient-define-infix, because they are useful in more
than one menu, and are then referenced by name in the definitions of
these menus.)

Therefore, the addition to read-extended-command-predicate is necessary,
whether I avoid interning the "anonymous" commands or not.  And as a
consequence of that, there would be zero benefit to avoiding to intern
the "anonymous" commands; but many drawbacks.

I have therefore decided to stick to using interned symbols.

Even if you don't agree with me by now, there is one big drawback that
moving away from interned symbols would have, which I believe will win
you over.  Each command is associated with an object and the symbol is
used to link the two.  As a consequence all "anonymous" commands that
set an argument can be mere aliases to the same command, which only has
to live in memory once.  If the symbol can no longer be used to link the
command and object, then the anonymous commands would have to be defined
as oclosures.  These closures would be nearly identical, but each one of
them would permanently use up memory.

-- Jonas