emacs-devel
[Top][All Lists]
Advanced

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

Re: combining cond and let, to replace pcase.


From: Axel Forsman
Subject: Re: combining cond and let, to replace pcase.
Date: Sun, 19 Nov 2023 16:08:08 +0000 (UTC)

Richard Stallman <rms@gnu.org> writes:

>   > I've thought before that it would be nice to have a cond-let construct,
>   > like if-let and when-let.
>
> Here's an example to show what I have come up with along these lines:
>
> (cond* (:bind (x foobar) y z (foo 5) a)  ;; variables to bind, as in let
>        ;; I'm also thinking of using nil instead of :bind.
>
>        ;; t as condition means run this clause unconditionally
>        ;; then continue with next clause.
>        (t do-this-unconditionally-and-dont-stop...)
>
>        ;; Tests with a new function `match' which I'm going to work on next.
>        ((match x 'foo) ...)
>
>        ;; t in the last clause means the same as in previous clauses,
>        ;; but you can think of it as being the same as in `cond'.
>        (t do-this-as-last-resort)
>        )
>
> Rhe idea is that conf* does part of pcase's added functionality, and
> `match' will do the rest.  But it is not necessary to cram _all_ the
> additional matching functionality (to compare to pcase) into a single
> new function. 
>
> We could have more than one new function, eacn for its own kind of
> pattern matching.
>
> Some of them could do destructuring as well as matching.

pcase is not concerned with your proposed :bind and t keywords. For a
discussion about pcase being too "large", it seems weird to suggest an
alternative that does even more. What am I missing?

Also, the various `match' constructs would have to be part of the conf*
macro as destructuring needs to bind variables, so I fail to see how
this would be an improvement over the status quo?

I may be misinterpreting what you are proposing here, and in that case I
apologize, but in general should one not try to understand the thing
they are attempting to replace, or else likely make the same mistakes?

> I hoipe that using a few constructs to divide up the job will avoid
> the kludginess of pcase's bells-and-whistles-for-everything approach,
> resulting in something equally convenient but made of simple components.

I will preface this by saying that I like pcase, I think it is one of
the better-written Emacs Lisp packages. History has shown that
declarative pattern matching à la ML is a good concept, with languages
on the "other side of the pond" such as Java; Python; C++; JavaScript;
and C#, all eventually adopting it. Any discussion about the purported
complexity of pcase should start with delineating the essentials of
pcase, (something I do not think this mailing thread has done a good job
of), which is that the pcase syntax for destructuring a value should
mirror the syntax for constructing that same value. E.g.

    `(if ,condition ,consequent ,alternative)

constructs a list if condition, consequent, and alternative are
variables currently bound, and is also a pcase pattern for
deconstructing that same list. IMO that is perfectly simple.

Does the addition of other pcase pattern-forms such as
pred/app/guard/and/or complicate things too much? I would say maybe;
it is certainly possible to abuse them.

On the other hand, it is very useful to match against an integer in some
range, nested withing a list, e.g.

    (pcase x
      (`(integer ,(and (pred (lambda (i) (< 0 i 5))) i)) (print i))
      (`(integer ,i) (do-something-else i))
      ...)

>From what I have seen from languages that do not have special syntax for
matching integers in ranges, this would usually be handled by guard
expressions as a second step after matching is done, e.g.

    (pcase x
      (`(integer ,i) :when (< 0 i 5)
        (print i))
      (`(integer ,i) (do-something-else i))
      ...)

Something like that would work to keep the patterns simpler.

Without pred/app/guard/and/or one could think of getting rid of the
messy backquote/comma syntax, for something cleaner based just on lists,
e.g.

    (pcase x
      (('integer i) (print i))
      (('if condition consequent alternative) (message "if"))
      ...)

however that would not good for a couple of reasons:

1. The syntax for constructing lists is no longer the same as for
   destructuring.
2. It would leave little room for incorporating syntax for destructuring
   cl-struct:s, etc. pcase has the (cl-struct TYPE &rest FIELDS) form
   for this purpose, which is what it is, but at least I think it is
   good that it exists.

This is all to say that I disagree with your claim that pcase has a
bells-and-whistles-for-everything approach. Rather it is maximally
compositional and based on the easy-to-learn and neat duality of
constructing/destructuring.
I also consider it a mistake to introduce forms such as

>        ;; t as condition means run this clause unconditionally
>        ;; then continue with next clause.
>        (t do-this-unconditionally-and-dont-stop...)

with non-descriptive names such as nil/t, that introduce side-effects in
the middle of matching something. Pattern matching is great because it
is declarative and makes it possible to communicate intent, rather than
concerning ourselves with what combination of car/cdr/equal/let/if is
needed to achieve what we want.



reply via email to

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