Subject: Re: bug#69270: 29.1; transient.el: Using `transient-define-prefix' with a lambda command results in warning

From: Jonas Bernoulli <[email-masked]>
Date: Sat, 01 Mar 2025 16:45:05 +0100

> Jonas, could you please look into this?

This is an issue in the Transient version bundled with Emacs 29.1.
It has been fixed since.  I don't remember when exactly it was fixed.
Maybe other 29.* releases are affected too, 30.1 is not.

Users who are bothered by this warning, who also want to stay on a 29.*
release, can install Transient from GNU ELPA.  Alternatively they can
ignore this warning; it points at an actual issue, but it is harmless.

(They should update to recent Transient release anyway.  There *are*
serious issues in past Transient releases, which are distributed with
past Emacs releases, but the warning at hand is not that.

I do make sure to keep current Transient release compatible with Emacs
releases starting with 28.1, the first release that bundled Transient,
so it is safe to use a recent Transient with an old Emacs.)

>> From: Fadi Moukayed <[email-masked]>
>> Date: Mon, 19 Feb 2024 11:45:25 +0100
>> 
>> Mentioned on the #emacs channel (libera).
>> 
>> As per https://www.gnu.org/software/emacs/manual/html_mono/transient.html#Suffix-Specifications
>> – it is mentioned that "COMMAND can also be a lambda expression."
>> 
>> However, evaluating the form (even on an Emacs instance started with "emacs -Q")
>> 
>>   (transient-define-prefix my-test-transient () ["Heading" ("a"
>> "choice a" (lambda () (interactive)))])
>> 
>> Results in the following warning being printed on the *Messages* buffer:
>> 
>>   Warning: (lambda nil \...) quoted with ' rather than with #'

Back in the 29.1 days, such embedded lambda were wrongly quoted by the
code that turned the group specification into actual code.  As a result
the lambda were not compiled and instead evaluated every time the lambda
is called.  The lambda still worked correctly but was a teeny tiny bit
less efficient.  Of course it is something that should be fixed (it is
no just immeasurably slower, but wrong™), and by now that has happened.

The byte-compiler noticed the issue and warns about this harmless, but
embarrassing issue.

>> Attempting to hashquote the lambda as in:
>> 
>>   (transient-define-prefix my-test-transient () ["Heading" ("a"
>> "choice a" (lambda () (interactive)))])
>> 
>> ... as suggested by some on #emacs results in an error when
>> evaluating.

Yes, that does not work here.  The evaluation rules are a bit different
in these group specification vectors than elsewhere.  To an extend, I
now regret having gone that way, but it is too late to change that now.

If I were to start over, I might require explicit unquoting:

(transient-define-prefix my-test-transient ()
  `["Heading"
    ("a" "choice a" ,(lambda () (interactive)))])

Alas, that is not how it works now.  This is a vector, so nothing in it
has to be quoted.  (If you do quote something, as you have attempted,
that doesn't work, because you end up with something that is quoted
twice.)

The embedded lambda *should* be evaluated, so in more recent Transient
versions, when the prefix is defined, we look for lambda in these
vectors, and make special arrangements for those to be treated as code
and not data.

Back in the 29.1 days that wasn't done yet, and that is what the warning
is about.

>> At this point, I am not sure if this is an error in documentation of
>> transient.el, or an error in the macro definition of the
>> `transient-define-prefix' macro itself, or if the warning is
>> superfluous and whether it should be ignored. As I'm unsure whether
>> this is a bug or not, I'm erring on the side of safety and reporting
>> it to bring it to attention.

It is harmless and can be ignored.

This bug report can be closed.