Subject: [PATCH] Add macro 'pcase-setq'

From: Okam <[email-masked]>
Date: Sun, 01 Aug 2021 17:20:41 +0000

Hello,

This patch adds a `setq`-like equivalent to `pcase-let`.  This is
convenient when one wants the bindings to exist outside of a `let` form.

This macro expands into multiple `setq` calls that are combined where
possible.

     ;; => (1 2 3 4)
     (let (a b c d)
       (pcase-setq a 1
                   b 2
                   `[,c ,d] [3 4])
       (list a b c d))

Please let me know what should be changed.

Thank you.
From 824d3b56b5f4bd1aac1b933c353715155edc7698 Mon Sep 17 00:00:00 2001
From: Earl Hyatt <[email-masked]>
Date: Sun, 1 Aug 2021 12:33:14 -0400
Subject: [PATCH] Add macro 'pcase-setq'

* doc/lispref/control.texi: Document this macro.
* lisp/emacs-lisp/pcase.el: Add this macro.

This macro is the 'setq' equivalent of 'pcase-let'.
---
 doc/lispref/control.texi |  4 ++++
 etc/NEWS                 |  4 ++++
 lisp/emacs-lisp/pcase.el | 41 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 49 insertions(+)

diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi
index 5026d0a4d7..a0540005d2 100644
--- a/doc/lispref/control.texi
+++ b/doc/lispref/control.texi
@@ -1312,6 +1312,10 @@ Destructuring with pcase Patterns
 up being equivalent to @code{dolist} (@pxref{Iteration}).
 @end defmac
 
+@defmac pcase-setq pattern value@dots{}
+Bind variables to values in a @code{setq} form, destructuring each
+@var{value} according to its respective @var{pattern}.
+@end defmac
 
 @node Iteration
 @section Iteration
diff --git a/etc/NEWS b/etc/NEWS
index 95a2c87d05..0f11caf512 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -553,6 +553,10 @@ The new 'cl-type' pattern compares types using 'cl-typep', which allows
 comparing simple types like '(cl-type integer)', as well as forms like
 '(cl-type (integer 0 10))'.
 
+*** New macro 'pcase-setq'
+This macro is the 'setq' equivalent of 'pcase-let', which allows for
+destructuring patterns in a 'setq' form.
+
 +++
 ** profiler.el
 The results displayed by 'profiler-report' now have the usage figures
diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index 006517db75..f9665eba97 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -317,6 +317,47 @@ pcase-dolist
          (pcase-let* ((,(car spec) ,tmpvar))
            ,@body)))))
 
+;;;###autoload
+(defmacro pcase-setq (&rest args)
+  "Bind values by destructuring with `pcase'.
+
+\(fn PATTERN VALUE PATTERN VALUE ...)"
+  (declare (debug (&rest [pcase-PAT form])))
+  (let ((results)
+        (pattern)
+        (value))
+    (while args
+      (setq pattern (pop args)
+            value (pop args))
+      (push (if (pcase--trivial-upat-p pattern)
+                (list 'setq pattern value)
+              (pcase-compile-patterns
+               value
+               (list (cons pattern
+                           (lambda (varvals &rest _)
+                             (cons 'setq
+                                   (mapcan (lambda (varval)
+                                             (let ((var (car varval))
+                                                   (val (cadr varval)))
+                                               (list var val)))
+                                           varvals)))))))
+            results))
+
+    ;; Combine multiple calls to `setq' to a single call where we can.
+    (let ((returned-setqs)
+          (combining))
+      (dolist (i results)
+        (if (eq (car i) 'setq)
+            (push (cdr i) combining)
+          (when combining
+            (push `(setq ,@(apply #'append combining))
+                  returned-setqs)
+            (setq combining nil))
+          (push i returned-setqs)))
+      (when combining
+        (push `(setq ,@(apply #'append combining))
+              returned-setqs))
+      (macroexp-progn returned-setqs))))
 
 (defun pcase--trivial-upat-p (upat)
   (and (symbolp upat) (not (memq upat pcase--dontcare-upats))))
-- 
2.25.1