Subject: bug#72344: closed (Re: bug#72344: [PATCH] Add a version of cl-once-only which handles lists of forms)

From: [email-masked] (GNU bug Tracking System)
Date: Tue, 08 Apr 2025 05:11:01 +0000

Your bug report

#72344: [PATCH] Add a version of cl-once-only which handles lists of forms

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
72344: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=72344
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#72344: [PATCH] Add a version of cl-once-only which handles lists of forms
From: Sean Whitton <[email-masked]>
To: Thuna <[email-masked]>, [email-masked]
Date: Tue, 08 Apr 2025 13:10:05 +0800
List-Id: <debbugs-submit.debbugs.gnu.org>

Hello,

On Sun 28 Jul 2024 at 11:17pm +02, Thuna wrote:

> (My motivation for this is to use this macro in another patch which I
> didn't want to make longer with out-of-scope additions.  If this is
> considered not worth polluting the `cl-' namespace over I can just write
> the forms by hand so it's fine either way.)

This discussion hasn't been active for almost a year now.
The proposed cl-once-only-multiple is indeed fairly idiosyncratic and
unlikely to see much use, so I think we should probably just close this
request for now.  Going ahead and doing that.

-- 
Sean Whitton

Subject: [PATCH] Add a version of cl-once-only which handles lists of forms
From: Thuna <[email-masked]>
To: [email-masked]
Date: Sun, 28 Jul 2024 23:17:31 +0200
List-Id: <debbugs-submit.debbugs.gnu.org>

Attached is the macro `cl-once-only*', which takes a symbol which is
bound to a list of form (such as those contained in a `&rest args' in
`defmacro') and binds that variable to a list of symbols where each
symbol is bound to the result of evaluating (in order) the corresponding
form.  The docstring provides an example which might illustrate what it
does better then I did here.

I don't expect this to be used too often but it could be somewhat handy
in macros with a `&rest args' which are supposed to be evaluated.

(My motivation for this is to use this macro in another patch which I
didn't want to make longer with out-of-scope additions.  If this is
considered not worth polluting the `cl-' namespace over I can just write
the forms by hand so it's fine either way.)

>From e8af4374454965d541ad3b8062e6793217d4e86f Mon Sep 17 00:00:00 2001
From: Thuna <[email-masked]>
Date: Sun, 28 Jul 2024 22:26:41 +0200
Subject: [PATCH] Add a version of cl-once-only which handles lists of forms

* lisp/emacs-lisp/cl-macs.el (cl-once-only*): Define macro.
---
 lisp/emacs-lisp/cl-macs.el | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 2e501005bf7..70d66b3a250 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -2544,6 +2544,43 @@ cl-once-only
                           collect `(,(car name) ,gensym))
              ,@body)))))
 
+(defmacro cl-once-only* (listvar &rest body)
+  "Generate code to evaluate the list of forms LISTVAR just once in BODY.
+
+This is a macro to be used while defining other macros.  LISTVAR should
+be a variable which holds a list of forms, such as ARGS in a function
+call of the form \\=`(,head ,@args).  Within BODY, the variable LISTVAR
+will be bound to a list of uninterned symbols of the same length, all of
+which are variables in the final macroexpansion which are bound to the
+result of evaluating the corresponding form.
+
+For example, the following macro
+
+  (defmacro my-list (head &rest args)
+    (cl-once-only* args
+      \\=`(list (,head ,@args) ,@args)))
+
+when called like
+
+  (let ((x \\='(1 5 4)))
+    (my-list + (pop x) (1+ (pop x)) (1- (pop x))))
+
+will expand into
+
+  (let ((x \\='(1 5 4)))
+    (let* ((arg1 (pop x)) (arg2 (1+ (pop x))) (arg3 (1- (pop x))))
+      (list (+ arg1 arg2 arg3) arg1 arg2 arg3)))
+
+and the arguments will be evaluated only once and in order."
+  (declare (debug (arg body)) (indent 1))
+  (let ((args (gensym (symbol-name listvar))))
+    `(let ((,args
+            (cl-loop for i from 1 to (length ,listvar) collect
+                     (make-symbol (format "%s$%d" ',(symbol-name args) i)))))
+       `(let* ,(cl-mapcar #'list ,args ,listvar)
+          ,(let ((,listvar ,args))
+             ,@body)))))
+
 ;;; Multiple values.
 
 ;;;###autoload
-- 
2.44.2