emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Code for cond*


From: Stefan Monnier
Subject: Re: Code for cond*
Date: Tue, 23 Jan 2024 13:10:01 -0500
User-agent: Gnus/5.13 (Gnus v5.13)

> Here is the first draft of cond*.  I have tested some cases
> but I ask others to help in testing it more thoroughly.

AFAICT the main reason to oppose the use of `pcase` is that it increases
the size of the language, making it harder for non-experts to
read/understand/modify ELisp code.

`cond*` suffers from the same criticism.  And having both even more so
(and whether you like it or not, `pcase` is probably not going to
disappear any time soon, among other things because it offers a style
familiar from other languages and that style is gaining popularity).

So, I'm not super enthusiastic about adding such a new form, but being
responsible for the introduction of several such new constructs in ELisp
over the years, I do feel a bit like the pot calling the kettle black.

So, rather than oppose it, I'll just point out some things which I think
could be improved (or which offend my taste, as the case may be).

>   (cond* 
>        ;; Same as a clause in `cond',
>        (CONDITION
>         DO-THIS-IF-TRUE-THEN-EXIT...)

So far so good.

>        ;; Execute FORM, and ignore its value
>        ;; (except if this is the last clause).
>        (FORM)

YAGNI.

>        ;; Variables to bind, as in let*, around the rest
>        ;; of the cond*.
>        ((bind* (x foobar) y z (foo 5) a))
>        ;; Bindings continue in effect for the whole cond* construct.

This is one of the functionality missing from `cond`, so I'm rather
favorable, but:
- why `bind*` when the rest of ELisp uses `let*` in the names of all
  related constructs?
- Why the double parens?
- Why `*`?  If you need such sequential bindings, you can get it with

    (cond
     [...]
     ((bind* (x E1)))
     ((bind* (y (foo x))))

  So here a plain old "parallel let" would be more useful (or restrict
  that binding to a single var, so the question doesn't even pop up).

>        ;; Bind variables for the clause, as in let*.
>        ((bind* (foo 5) (condition (hack-it foo)))
>         ;; condition is that the last variable bound be non-nil.
>         BODY...)

This is the other of the functionality missing from `cond`, so I'm
rather favorable, but:
- why `bind*` when the rest of ELisp uses "let*" in the names of all
  related constructs?
- I'm not happy about using the same construct for "bindings
  for this clause" and "bindings for subsequent clauses".
Maybe it should be inspired by `when-let*`?

>        ;; Extracts substructure and binds variables for the clause.
>        ((match* `(expt ,foo ,bar) x)
>         DO-THIS-IF-IT-MATCHED-THEN-EXIT...)
>        ;; Bindings no longer in effect.

This `(expt ,foo ,bar) is a valid Pcase pattern, so it would be odd if
other Pcase patterns can't be used here.
For that same reason, it's odd for its name not to include "pcase".

>        ((match* `(expt ,foo ,bar) x))
>        ;; Bindings continue in effect.

Same comment as before: I like having both "bindings for the clause"
and "bindings for the rest", but having the two be syntactically
identical will lead to confusion.

Finally, I see a fairly extensive but hardcoded pattern language, which
seems like a regression compared to Pcase where the pattern language is
built from a few primitives only (with the rest defined on top via
`pcase-defmacro`).  The worst part, tho, is that the two pattern
languages are very similar, and I can't see any good reason for
the differences.  It feels like an NIH reaction.


        Stefan




reply via email to

[Prev in Thread] Current Thread [Next in Thread]