Subject: Re: bug#73853: Should and-let* become a synonym for when-let*?

From: Stefan Monnier <[email-masked]>
Date: Tue, 21 Jan 2025 19:08:26 -0500

>> Then write it with `when-let` and get used to the idea that it returns
>> a value, because... well, it does and code out there relies on it (a
>> small random sampling suggests that around 15% of the existing uses in
>> Emacs do).
> But Stefan, if we replace calls of `and-let' with `when-let', I would
> not only have to get used to the idea "that it returns a value", but I
> also would have to remember what happens when `when-let' is used as a
> condition (e.g. in an `if') with an empty body.

Of course, the same happens as happens in any other position.  This is
a general rule in pretty much all programming languages: the evaluation
is performed in the same way regardless on the context where that
expression is used.

> I would always ask: which of the semantics did we choose again...?

That's easy to fix: disallow or strongly discourage empty bodies.
We already do that in various places and it would be very natural to do
that in `when-let*` as well.
Currently in Emacs itself, `grep` finds 681 uses of `when-let(*)` and
only one of them has an empty body (and I'd argue it's an error: the
last "binding" is `((push finext extlist-new))` which macro expands to
something silly; not sure if the bytecompiler manages to unsillify the
code).
The score is a bit less one-sided for `and-let*` where I count 25 empty
bodies among 140 uses (the vast majority of them in ERC and Tramp).

Also, it's easier to remember the answer if you have to remember it only
for `when-let*` rather than having to remember it for both `and-let*` and
`when-let*`.

Incidentally, `and-let*` is member of a rather small club of forms where
en empty body is not treated as a body that returns nil.  I was able
to remember only 3 other forms that have this property (one of which is
never used, AFAICT).  I don't think forms should wear this property as
a badge of honor.

> This is not better than what you currently dislike.  I would then have
> to remember and consult the doc instead of you.  It seems a bit that
> you assume that I can remember things with much less effort than you.

The rule is easy: an empty body returns nil.
It applies to *almost* all ELisp forms that include a "body"
(i.e. a list of forms).

> I mean, there is only one "subtle difference" between `and-let' and
> `when-let' - the handling of an empty body - right?  Isn't it absolutely
> "clear" what an empty body means in both cases?

[ Given the fact that in almost all other cases an empty body returns nil,
  I wouldn't say it's "absolutely clear" for `and-let*`, no.  ]

Michael Heerdegen [2025-01-19 06:08:37] wrote:
> What makes `pcase's let/app/pred/guard different from and/when/if-let?

The differences between let/app/pred/guard and comparable to the
differences between if/when/and/unless/cond: you can fairly easily get
each one from (some of) the others, but it's not mere renaming, so
there's a tradeoff because of code size/verbosity (and also ease of
optimization, tho to a minor degree).

In the case of `and-let*`, it seems the tradeoff is not code size nor
verbosity, but only a reluctance by some developers to rely on
`when(-let)`s return value, even tho it's documented/reliable.

So maybe instead of converting `and-let*` to `when-let*` we should
recommend converting them to `if-let*`, where I don't think anyone has
ever expressed an expectation that `if(-let)` doesn't return a value or
that it's unclear what value it would return if the (else) body is empty.
[ A quick search suggests there are very few uses (I could find only
  4 in Emacs) of `and-let*` with a body containing more than one
  form.  ]

> There is likewise lots of redundancy, and the subtle differences are
> hard to grasp and remember for a lot of people.  Would you remove the
> two least used ones?

I've been tempted to do that, but they're used often enough and the
rewrite is sufficiently more verbose that I've kept them so far.

> So isn't your personal taste in this case here relevant too, for what
> you want to do?

Definitely.  I did not intend to imply otherwise.

        Stefan