GNU bug report logs - #75594
31.0.50; [FR]: Change equality test for Pcase non-linear patterns

Previous Next

Package: emacs;

Reported by: Eshel Yaron <me <at> eshelyaron.com>

Date: Wed, 15 Jan 2025 20:20:02 UTC

Severity: wishlist

Found in version 31.0.50

To reply to this bug, email your comments to 75594 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to monnier <at> iro.umontreal.ca, michael_heerdegen <at> web.de, bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Wed, 15 Jan 2025 20:20:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Eshel Yaron <me <at> eshelyaron.com>:
New bug report received and forwarded. Copy sent to monnier <at> iro.umontreal.ca, michael_heerdegen <at> web.de, bug-gnu-emacs <at> gnu.org. (Wed, 15 Jan 2025 20:20:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Eshel Yaron <me <at> eshelyaron.com>
To: bug-gnu-emacs <at> gnu.org
Subject: 31.0.50; [FR]: Change equality test for Pcase non-linear patterns
Date: Wed, 15 Jan 2025 21:19:09 +0100
Hi,

I want to use non-linear pcase patterns to match against source code
with el-search, and I find the existing equality test that Pcase
generates in such cases too strict (it uses eql, and I need equal).  

I can use (pred (equal foo)) patterns instead of just repeating foo,
but I'm generating the pattern programmatically and I'd rather avoid
special-casing the first foo occurrence.

What do you think about (1) switching to equal, or (2) allowing pcase
callers to specify the equality predicate?

I can imagine (1) having compatibility implications and (2) having
performance implications, but I don't have a good sense for how
significant these would be.

As one data point, the following diff seems to enable my use case,
without breaking Emacs bootstrap nor pcase-tests.el:

diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index a6a4751f49a..4f0fe6b64ab 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -1055,7 +1055,7 @@ pcase--u1
               (pcase--u1 matches code (cons (list upat sym) vars) rest)
             ;; Non-linear pattern.  Turn it into an `eq' test.
             (setcdr (cdr v) 'used)
-            (pcase--u1 (cons `(match ,sym . (pred (eql ,(cadr v))))
+            (pcase--u1 (cons `(match ,sym . (pred (equal ,(cadr v))))
                              matches)
                        code vars rest))))
        ((eq (car-safe upat) 'app)



Thanks,

Eshel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Wed, 15 Jan 2025 22:27:02 GMT) Full text and rfc822 format available.

Message #8 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Eshel Yaron <me <at> eshelyaron.com>
To: Eshel Yaron via "Bug reports for GNU Emacs, the Swiss army knife of text
 editors" <bug-gnu-emacs <at> gnu.org>
Cc: Michael Heerdegen <michael_heerdegen <at> web.de>, 75594 <at> debbugs.gnu.org,
 Stefan Monnier <monnier <at> iro.umontreal.ca>
Subject: Re: bug#75594: 31.0.50; [FR]: Change equality test for Pcase
 non-linear patterns
Date: Wed, 15 Jan 2025 23:26:19 +0100
Eshel Yaron writes:

> What do you think about (1) switching to equal, or (2) allowing pcase
> callers to specify the equality predicate?
>
> I can imagine (1) having compatibility implications and (2) having
> performance implications, but I don't have a good sense for how
> significant these would be.

On second thought, it should be doable without affecting existing code.
The patch below adds a pattern (cheq EQ) that succeeds while changing
the equality predicate to EQ for subsequent patterns on the same branch.

With it, we get:

--8<---------------cut here---------------start------------->8---
(pcase '((foo) (foo))
  ((and (cheq equal) `(,bar ,bar ,bar)) 'a)
  (`(,bar ,bar)                         'b)
  ((and (cheq equal) `(,bar ,bar))      'c))
=> c
--8<---------------cut here---------------end--------------->8---

So I can just wrap my pattern in (and (cheq equal) ...) to use equal for
non-linear patterns.

WDYT?


diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index a6a4751f49a..483aa9b8434 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -522,7 +522,7 @@ pcase--macroexpand
     (cond
      ((null head)
       (if (pcase--self-quoting-p pat) `',pat pat))
-     ((memq head '(pred guard quote)) pat)
+     ((memq head '(pred guard quote cheq)) pat)
      ((memq head '(or and)) `(,head ,@(mapcar #'pcase--macroexpand (cdr pat))))
      ((eq head 'app) `(app ,(nth 1 pat) ,(pcase--macroexpand (nth 2 pat))))
      (t
@@ -591,6 +591,8 @@ pcase--if
    ((eq then :pcase--dontcare) `(progn (ignore ,test) ,else))
    (t (macroexp-if test then else))))
 
+(defvar pcase--equal #'eql)
+
 ;; Note about MATCH:
 ;; When we have patterns like `(PAT1 . PAT2), after performing the `consp'
 ;; check, we want to turn all the similar patterns into ones of the form
@@ -625,7 +627,8 @@ pcase--u
     (let* ((carbranch (car branches))
            (match (car carbranch)) (cdarbranch (cdr carbranch))
            (code (car cdarbranch))
-           (vars (cdr cdarbranch)))
+           (vars (cdr cdarbranch))
+           (pcase--equal #'eql))
       (pcase--u1 (list match) code vars (cdr branches)))))
 
 (defun pcase--and (match matches)
@@ -1053,9 +1056,9 @@ pcase--u1
         (let ((v (assq upat vars)))
           (if (not v)
               (pcase--u1 matches code (cons (list upat sym) vars) rest)
-            ;; Non-linear pattern.  Turn it into an `eq' test.
+            ;; Non-linear pattern.  Turn it into an equality test.
             (setcdr (cdr v) 'used)
-            (pcase--u1 (cons `(match ,sym . (pred (eql ,(cadr v))))
+            (pcase--u1 (cons `(match ,sym . (pred (,pcase--equal ,(cadr v))))
                              matches)
                        code vars rest))))
        ((eq (car-safe upat) 'app)
@@ -1112,6 +1115,9 @@ pcase--u1
                      (pcase--u rest))
                    vars
                    (list `((and . ,matches) ,code . ,vars))))
+       ((eq (car-safe upat) 'cheq)
+        (let ((pcase--equal (cadr upat)))
+          (pcase--u1 matches code vars rest)))
        (t (error "Unknown pattern `%S'" upat)))))
    (t (error "Incorrect MATCH %S" (car matches)))))
 




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Wed, 15 Jan 2025 22:27:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Thu, 16 Jan 2025 00:49:01 GMT) Full text and rfc822 format available.

Message #14 received at 75594 <at> debbugs.gnu.org (full text, mbox):

From: Stefan Monnier <monnier <at> iro.umontreal.ca>
To: Eshel Yaron <me <at> eshelyaron.com>
Cc: Michael Heerdegen <michael_heerdegen <at> web.de>, 75594 <at> debbugs.gnu.org
Subject: Re: bug#75594: 31.0.50; [FR]: Change equality test for Pcase
 non-linear patterns
Date: Wed, 15 Jan 2025 19:48:14 -0500
> I want to use non-linear pcase patterns to match against source code
> with el-search, and I find the existing equality test that Pcase
> generates in such cases too strict (it uses eql, and I need equal).

Funny, I was reminded yesterday that this equality test is `eql` whereas
Pcase uses `equal` "everywhere else".

> What do you think about (1) switching to equal,

On the principle, I'm in favor (I think that's what we should have
used all along), but the problem is the risk of breaking existing code.

> or (2) allowing pcase callers to specify the equality predicate?
[...]
> The patch below adds a pattern (cheq EQ) that succeeds while changing
> the equality predicate to EQ for subsequent patterns on the same branch.

Making the semantics of the VAR pattern depend on whether some other
pattern has been seen earlier or not strikes me as hideous (tho clearly
the non-linearity-becomes-an-equality-constraint is exactly doing that).

I can see two possible syntax options:

1- some kind of "contextual" pcase macro like

    (with-equality-predicate #'equal
      `(,foo ,foo ,foo))

2- some kind of VAR pattern but annotated with an equality predicate in
   case this is not the first use of this identifier, so you'd write

    `(,(var foo #'equal) ,(var foo #'equal) ,(var foo #'equal))


Stefan





Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Thu, 16 Jan 2025 06:51:02 GMT) Full text and rfc822 format available.

Message #17 received at 75594 <at> debbugs.gnu.org (full text, mbox):

From: Eshel Yaron <me <at> eshelyaron.com>
To: Stefan Monnier <monnier <at> iro.umontreal.ca>
Cc: Michael Heerdegen <michael_heerdegen <at> web.de>, 75594 <at> debbugs.gnu.org
Subject: Re: bug#75594: 31.0.50; [FR]: Change equality test for Pcase
 non-linear patterns
Date: Thu, 16 Jan 2025 07:50:39 +0100
Stefan Monnier <monnier <at> iro.umontreal.ca> writes:

>> I want to use non-linear pcase patterns to match against source code
>> with el-search, and I find the existing equality test that Pcase
>> generates in such cases too strict (it uses eql, and I need equal).
>
> Funny, I was reminded yesterday that this equality test is `eql` whereas
> Pcase uses `equal` "everywhere else".
>
>> What do you think about (1) switching to equal,
>
> On the principle, I'm in favor (I think that's what we should have
> used all along), but the problem is the risk of breaking existing code.
>
>> or (2) allowing pcase callers to specify the equality predicate?
> [...]
>> The patch below adds a pattern (cheq EQ) that succeeds while changing
>> the equality predicate to EQ for subsequent patterns on the same branch.
>
> Making the semantics of the VAR pattern depend on whether some other
> pattern has been seen earlier or not strikes me as hideous (tho clearly
> the non-linearity-becomes-an-equality-constraint is exactly doing that).
>
> I can see two possible syntax options:
>
> 1- some kind of "contextual" pcase macro like
>
>     (with-equality-predicate #'equal
>       `(,foo ,foo ,foo))
>
> 2- some kind of VAR pattern but annotated with an equality predicate in
>    case this is not the first use of this identifier, so you'd write
>
>     `(,(var foo #'equal) ,(var foo #'equal) ,(var foo #'equal))

Both options sound good.  The need to repeat the equality predicate
multiple times in option 2 makes it slightly less preferable, but it
maybe it'd be easier to document/explain.


Thanks,

Eshel






Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Fri, 17 Jan 2025 01:01:02 GMT) Full text and rfc822 format available.

Message #20 received at 75594 <at> debbugs.gnu.org (full text, mbox):

From: Michael Heerdegen <michael_heerdegen <at> web.de>
To: Eshel Yaron <me <at> eshelyaron.com>
Cc: 75594 <at> debbugs.gnu.org, Stefan Monnier <monnier <at> iro.umontreal.ca>
Subject: Re: bug#75594: 31.0.50; [FR]: Change equality test for Pcase
 non-linear patterns
Date: Fri, 17 Jan 2025 02:01:47 +0100
Eshel Yaron <me <at> eshelyaron.com> writes:

> > I can see two possible syntax options:
> >
> > 1- some kind of "contextual" pcase macro like
> >
> >     (with-equality-predicate #'equal
> >       `(,foo ,foo ,foo))
> >
> > 2- some kind of VAR pattern but annotated with an equality predicate in
> >    case this is not the first use of this identifier, so you'd write
> >
> >     `(,(var foo #'equal) ,(var foo #'equal) ,(var foo #'equal))
>
> Both options sound good.  The need to repeat the equality predicate
> multiple times in option 2 makes it slightly less preferable, but it
> maybe it'd be easier to document/explain.

2 has the advantage that it allows mixing different predicates.  Dunno
if this is often useful, though.

I wonder how typical examples of this feature look like.  So far I
stumbled over the implicit equivalence test not being what I needed
sometimes, but not really often.

(Of course I'm open to add something (maybe more) convenient to el-search
if it is useful.)


Michael.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Fri, 17 Jan 2025 23:09:02 GMT) Full text and rfc822 format available.

Message #23 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Michael Heerdegen <michael_heerdegen <at> web.de>
To: Michael Heerdegen via "Bug reports for GNU Emacs, the Swiss army knife
 of text editors" <bug-gnu-emacs <at> gnu.org>
Cc: 75594 <at> debbugs.gnu.org, Eshel Yaron <me <at> eshelyaron.com>,
 Stefan Monnier <monnier <at> iro.umontreal.ca>
Subject: Re: bug#75594: 31.0.50; [FR]: Change equality test for Pcase
 non-linear patterns
Date: Sat, 18 Jan 2025 00:09:14 +0100
Hello again,

> > > 1- some kind of "contextual" pcase macro like
> > >
> > >     (with-equality-predicate #'equal
> > >       `(,foo ,foo ,foo))
> > >
> > > 2- some kind of VAR pattern but annotated with an equality predicate in
> > >    case this is not the first use of this identifier, so you'd write
> > >
> > >     `(,(var foo #'equal) ,(var foo #'equal) ,(var foo #'equal))

Maybe something like

(with-equality-predicate ((foo . #'equal))
  `(,foo ,foo ,foo))

?


Michael.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Fri, 17 Jan 2025 23:09:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Sun, 19 Jan 2025 07:33:01 GMT) Full text and rfc822 format available.

Message #29 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Eshel Yaron <me <at> eshelyaron.com>
To: Michael Heerdegen <michael_heerdegen <at> web.de>
Cc: 75594 <at> debbugs.gnu.org, "Michael Heerdegen via Bug reports for GNU Emacs,
 the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org>,
 Stefan Monnier <monnier <at> iro.umontreal.ca>
Subject: Re: bug#75594: 31.0.50; [FR]: Change equality test for Pcase
 non-linear patterns
Date: Sun, 19 Jan 2025 08:32:09 +0100
Hi,

Michael Heerdegen <michael_heerdegen <at> web.de> writes:

> Hello again,
>
>> > > 1- some kind of "contextual" pcase macro like
>> > >
>> > >     (with-equality-predicate #'equal
>> > >       `(,foo ,foo ,foo))
>> > >
>> > > 2- some kind of VAR pattern but annotated with an equality predicate in
>> > >    case this is not the first use of this identifier, so you'd write
>> > >
>> > >     `(,(var foo #'equal) ,(var foo #'equal) ,(var foo #'equal))
>
> Maybe something like
>
> (with-equality-predicate ((foo . #'equal))
>   `(,foo ,foo ,foo))
>
> ?

I'd rather not specify all affected variables explicitly, if that's what
this syntax implies.  The most important use case is to convert entire
pcase patterns to use equal semantics.  So transforming an arbitrary
pcase pattern to one where all non-linear patterns use equal should be
as simple as possible.  (Accordingly, I tend to "vote" for simply
changing the semantics to use equal, so no transformation is needed.)


Regards,

Eshel




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Sun, 19 Jan 2025 07:33:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Sun, 19 Jan 2025 22:37:01 GMT) Full text and rfc822 format available.

Message #35 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Michael Heerdegen <michael_heerdegen <at> web.de>
To: Eshel Yaron <me <at> eshelyaron.com>
Cc: 75594 <at> debbugs.gnu.org, "Michael Heerdegen via Bug reports for GNU Emacs,
 the Swiss army knife of text editors" <bug-gnu-emacs <at> gnu.org>,
 Stefan Monnier <monnier <at> iro.umontreal.ca>
Subject: Re: bug#75594: 31.0.50; [FR]: Change equality test for Pcase
 non-linear patterns
Date: Sun, 19 Jan 2025 23:37:05 +0100
Eshel Yaron <me <at> eshelyaron.com> writes:

> > (with-equality-predicate ((foo . #'equal))
> >   `(,foo ,foo ,foo))
> > ?
>
> I'd rather not specify all affected variables explicitly, if that's what
> this syntax implies.  The most important use case is to convert entire
> pcase patterns to use equal semantics.  So transforming an arbitrary
> pcase pattern to one where all non-linear patterns use equal should be
> as simple as possible.

We could additionally support (with-equality-predicate #'equal ...)

> (Accordingly, I tend to "vote" for simply
> changing the semantics to use equal, so no transformation is needed.)

This would be a backward-incompatible change so it's probably not
eligible for voting.


Michael.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#75594; Package emacs. (Sun, 19 Jan 2025 22:37:02 GMT) Full text and rfc822 format available.

Severity set to 'wishlist' from 'normal' Request was from Stefan Kangas <stefankangas <at> gmail.com> to control <at> debbugs.gnu.org. (Tue, 21 Jan 2025 02:27:05 GMT) Full text and rfc822 format available.

This bug report was last modified 151 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.