all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Okam via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org>
To: Lars Ingebrigtsen <larsi@gnus.org>, Nicolas Petton <nicolas@petton.fr>
Cc: 49407@debbugs.gnu.org, Stefan Monnier <monnier@iro.umontreal.ca>
Subject: bug#49407: Request: Specify default values in `map-let` in Map.el
Date: Fri, 21 Jul 2023 02:56:19 +0000	[thread overview]
Message-ID: <08a00b74-56f9-bb81-0411-36f9da8cf2d6@protonmail.com> (raw)
In-Reply-To: <87eec0klhu.fsf@gnus.org>

[-- Attachment #1: Type: text/plain, Size: 1659 bytes --]

On 2021-07-15 08:51 UTC, Lars Ingebrigtsen wrote:
> Okam <okamsn@protonmail.com> writes:
> 
>> `map-let` allows one to conveniently bind variables using `map-elt`, but
>> does not provide a way specify a default value if a key is missing.
>>
>> With `map-elt`, one can use the optional third argument to specify this
>> value.  It would be good to have this in `map-let` as well.
>>
>> For example, maybe it could look something like
>>
>>       ;; As just a third value in the list:
>>       (let ((map '(:a 1 :b 2)))
>>         (map-let ((:a a)
>>                   (:b b)
>>                   (:c c 3))
>>             map
>>           (+ a b c)))
> 
> Hm...  I guess that could be useful.  I've added Nicolas to the CCs;
> perhaps he has an opinion here.
> 
> --
> (domestic pets only, the antidote for overdose, milk.)
>     bloggy blog: http://lars.ingebrigtsen.no

Hello,

I've written a patch and tests that would add an optional third argument 
for a default value. I also changed the documentation string for the 
pcase pattern, which said that unfound keys are ignored and won't cause 
the pattern to fail. That didn't seem true for patterns that didn't 
match nil. For example, this won't match:

(pcase '(:two 2)
   ((map (:two two 33)
         (:three `(,a . ,b)))
    (list two a b))
   (_ 'fail))

However, I have a question about avoiding using a lambda. I see that the 
pattern uses `pcase--flip`, which is "used internally to avoid (funcall 
(lambda ...) ...)". Why should lambda functions be avoided for this, and 
I should be using a custom helper function for this one pattern?

Thank you.


[-- Attachment #2: 0001-Allow-default-values-in-map-let-and-the-pcase-map-fo.patch --]
[-- Type: text/x-patch, Size: 6159 bytes --]

From 1dbb797a9a736fc81ad9f672da815e2510e7f31a Mon Sep 17 00:00:00 2001
From: Earl Hyatt <okamsn@protonmail.com>
Date: Thu, 20 Jul 2023 21:44:41 -0400
Subject: [PATCH] Allow default values in 'map-let' and the pcase 'map' form

* lisp/emacs-lisp/map.el (map-let, map)
(map--make-pcase-bindings): Add a third argument for specifying a
default value, like in 'map-elt'. (Bug#49407)

* lisp/emacs-lisp/map.el (map--make-pcase-bindings): Clarify that keys
that aren't found aren't ignored, they actually get the value
nil (unless the new default value is given). The overall pattern can
still fail to match if the sub-pattern for the unfound key doesn't
match nil.

* test/lisp/emacs-lisp/map-tests.el (test-map-let-default)
(test-map-plist-pcase-default, test-map-pcase-matches): Add tests,
including for the above item.
---
 lisp/emacs-lisp/map.el            | 31 ++++++++++++------
 test/lisp/emacs-lisp/map-tests.el | 52 +++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+), 10 deletions(-)

diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el
index 7a48ba47434..7f127f312de 100644
--- a/lisp/emacs-lisp/map.el
+++ b/lisp/emacs-lisp/map.el
@@ -50,18 +50,23 @@ map
 
 ARGS is a list of elements to be matched in the map.
 
-Each element of ARGS can be of the form (KEY PAT), in which case KEY is
-evaluated and searched for in the map.  The match fails if for any KEY
-found in the map, the corresponding PAT doesn't match the value
-associated with the KEY.
+Each element of ARGS can be of the form (KEY PAT) or (KEY PAT DEFAULT),
+
+in which case KEY is evaluated and searched for in the map and
+DEFAULT is the evaluated value used if KEY is not found.  The
+match fails if for any KEY found in the map, the corresponding
+PAT doesn't match the value associated with the KEY.  The match
+fails if for any KEY not found in the map, the corresponding PAT
+doesn't match the DEFAULT value, if given.
 
 Each element can also be a SYMBOL, which is an abbreviation of
 a (KEY PAT) tuple of the form (\\='SYMBOL SYMBOL).  When SYMBOL
 is a keyword, it is an abbreviation of the form (:SYMBOL SYMBOL),
 useful for binding plist values.
 
-Keys in ARGS not found in the map are ignored, and the match doesn't
-fail."
+When no DEFAULT value is given, the default value for keys in
+ARGS not found in the map is nil, and the match doesn't fail so
+long at PAT, if given, matches nil."
   `(and (pred mapp)
         ,@(map--make-pcase-bindings args)))
 
@@ -71,12 +76,14 @@ map-let
 KEYS can be a list of symbols, in which case each element will be
 bound to the looked up value in MAP.
 
-KEYS can also be a list of (KEY VARNAME) pairs, in which case
-KEY is an unquoted form.
+KEYS can also be a list of (KEY VARNAME) pairs and
+\(KEY VARNAME DEFAULT) triples, in which case KEY is an
+unquoted form.
 
 MAP can be an alist, plist, hash-table, or array."
   (declare (indent 2)
-           (debug ((&rest &or symbolp ([form symbolp])) form body)))
+           (debug ((&rest &or symbolp ([form symbolp &optional form]))
+                   form body)))
   `(pcase-let ((,(map--make-pcase-patterns keys) ,map))
      ,@body))
 
@@ -599,7 +606,11 @@ map--make-pcase-bindings
   "Return a list of pcase bindings from ARGS to the elements of a map."
   (mapcar (lambda (elt)
             (cond ((consp elt)
-                   `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt)))
+                   (if (cddr elt)
+                       `(app (lambda (arg)
+                               (map-elt arg ,(car elt) ,(caddr elt)))
+                             ,(cadr elt))
+                     `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt))))
                   ((keywordp elt)
                    (let ((var (intern (substring (symbol-name elt) 1))))
                      `(app (pcase--flip map-elt ,elt) ,var)))
diff --git a/test/lisp/emacs-lisp/map-tests.el b/test/lisp/emacs-lisp/map-tests.el
index 86c0e9e0503..42458fbfeb7 100644
--- a/test/lisp/emacs-lisp/map-tests.el
+++ b/test/lisp/emacs-lisp/map-tests.el
@@ -577,6 +577,13 @@ test-map-let
     (should (= b 2))
     (should-not c)))
 
+(ert-deftest test-map-let-default ()
+  (map-let (('foo a 3)
+            ('baz b 4))
+      '((foo . 1))
+    (should (= a 1))
+    (should (= b 4))))
+
 (ert-deftest test-map-merge ()
   "Test `map-merge'."
   (should (equal (sort (map-merge 'list '(a 1) '((b . 2) (c . 3))
@@ -617,6 +624,51 @@ test-map-plist-pcase
                      (list one two))
                    '(1 2)))))
 
+(ert-deftest test-map-plist-pcase-default ()
+  (let ((plist '(:two 2)))
+    (should (equal (pcase-let (((map (:two two 33)
+                                     (:three three 44))
+                                plist))
+                     (list two three))
+                   '(2 44)))))
+
+(ert-deftest test-map-pcase-matches ()
+  (let ((plist '(:two 2)))
+    (should (equal (pcase plist
+                     ((map (:two two 33)
+                           (:three three))
+                      (list two three))
+                     (_ 'fail))
+                   '(2 nil)))
+
+    (should (equal (pcase plist
+                     ((map (:two two 33)
+                           (:three three 44))
+                      (list two three))
+                     (_ 'fail))
+                   '(2 44)))
+
+    (should (equal (pcase plist
+                     ((map (:two two 33)
+                           (:three `(,a . ,b) '(11 . 22)))
+                      (list two a b))
+                     (_ 'fail))
+                   '(2 11 22)))
+
+    (should (equal 'fail
+                   (pcase plist
+                     ((map (:two two 33)
+                           (:three `(,a . ,b) 44))
+                      (list two a b))
+                     (_ 'fail))))
+
+    (should (equal 'fail
+                   (pcase plist
+                     ((map (:two two 33)
+                           (:three `(,a . ,b) nil))
+                      (list two a b))
+                     (_ 'fail))))))
+
 (ert-deftest test-map-setf-alist-insert-key ()
   (let ((alist))
     (should (equal (setf (map-elt alist 'key) 'value)
-- 
2.34.1


  parent reply	other threads:[~2023-07-21  2:56 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-07-04 23:08 bug#49407: Request: Specify default values in `map-let` in Map.el Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-07-15  8:51 ` Lars Ingebrigtsen
2021-07-16  1:45   ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-07-21  2:56   ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors [this message]
2023-07-22  1:48     ` Michael Heerdegen
2023-07-22 15:45       ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-07-22 18:46       ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-07-26  3:41         ` Michael Heerdegen
2023-07-27  1:39           ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-07-28 18:02             ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-07-28 19:19               ` Eli Zaretskii
2023-07-29  0:37               ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-07-30 13:53                 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-07-31  4:02                   ` Michael Heerdegen
2023-08-08 12:41                     ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-08-06 13:31                   ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-08-08 12:46                     ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors
2023-07-27  1:37 ` Earl Hyatt via Bug reports for GNU Emacs, the Swiss army knife of text editors

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=08a00b74-56f9-bb81-0411-36f9da8cf2d6@protonmail.com \
    --to=bug-gnu-emacs@gnu.org \
    --cc=49407@debbugs.gnu.org \
    --cc=larsi@gnus.org \
    --cc=monnier@iro.umontreal.ca \
    --cc=nicolas@petton.fr \
    --cc=okamsn@protonmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.