unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Thuna <thuna.cing@gmail.com>
To: Sean Whitton <spwhitton@spwhitton.name>
Cc: 72344@debbugs.gnu.org
Subject: bug#72344: [PATCH] Add a version of cl-once-only which handles lists of forms
Date: Sun, 04 Aug 2024 00:40:22 +0200	[thread overview]
Message-ID: <871q35mdrd.fsf@gmail.com> (raw)
In-Reply-To: <87ikwi5neo.fsf@melete.silentflame.com>

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

> Thanks.  If you have indeed been coming across this in practice, then it
> seems worth adding a macro like this.
>
> Btw, I think we would want to see an update to cl.texi along with your
> change.

I've written a draft for what it could look like.  It needs proofreading
and editing, but it should be a fine-ish place to start from at least.
It uses `cl-once-only*' as the name of the macro but that's just a
placeholder for now.  (Do we want a NEWS entry also?)

I've also made it so that the first argument can either be of the form
(VARIABLE FORMS) or VARIABLE - how it worked before - which stands for
(VARIABLE VARIABLE), so as to keep parity with `cl-once-only'.

I've attached the change to cl.texi and the full implementation of
`cl-once-only*' as a single patch.

>> I don't necessarily think that that name is bad but it doesn't quite get
>> to the heart of the macro either.
>>
>> The macro is meant to be a `cl-once-only' which works on a list of
>> forms, and I feel that the "of forms" part is more important than the
>> "list" part, which is conceptually more likely to take on a meaning of
>> some ambiguous "collection" rather than a chain of cons cells.
>
> I think we should reserve the starred name for now.  As you said, your
> macro doesn't come up often, and we might later come up with a variant
> of cl-once-only that we want to use very often.

I see that point, and it is completely fair.  I have no problem
conceding the starred name.

> cl-seq-once-only ?

How about `cl-once-only-multiple'?  The name should probably be of the
form `cl-once-only<something>', rather than `cl-<something>-once-only' I
think.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Add-a-version-of-cl-once-only-which-works-on-a-list-.patch --]
[-- Type: text/x-patch, Size: 5005 bytes --]

From 18f72562f888d0abe56601544b23358429b7afcd Mon Sep 17 00:00:00 2001
From: Thuna <thuna.cing@gmail.com>
Date: Sun, 4 Aug 2024 00:30:44 +0200
Subject: [PATCH] Add a version of cl-once-only which works on a list of forms

---
 doc/misc/cl.texi           | 49 +++++++++++++++++++++++++++++++++++++-
 lisp/emacs-lisp/cl-macs.el | 48 +++++++++++++++++++++++++++++++++++++
 2 files changed, 96 insertions(+), 1 deletion(-)

diff --git a/doc/misc/cl.texi b/doc/misc/cl.texi
index 113029700ec..57e2f3a6c3b 100644
--- a/doc/misc/cl.texi
+++ b/doc/misc/cl.texi
@@ -999,7 +999,7 @@ Control Structure
 * Iteration::              @code{cl-do}, @code{cl-dotimes}, @code{cl-dolist}, @code{cl-do-symbols}.
 * Loop Facility::          The Common Lisp @code{loop} macro.
 * Multiple Values::        @code{cl-values}, @code{cl-multiple-value-bind}, etc.
-* Macro-Writing Macros::   @code{cl-with-gensyms}, @code{cl-once-only}.
+* Macro-Writing Macros::   @code{cl-with-gensyms}, @code{cl-once-only}, @code{cl-once-only*}.
 @end menu
 
 @node Assignment
@@ -2683,6 +2683,53 @@ Macro-Writing Macros
 @end example
 @end defmac
 
+@defmac cl-once-only* (variable forms) body
+This macro is a version of @code{cl-once-only} which takes a list of
+forms.  This macro is primarily meant to be used where the number of
+forms is unknown and thus @code{cl-once-only} cannot work, such as those
+obtained by a @code{&body} argument.
+
+Each element of @var{variable} may be used to refer to the result of
+evaluating the corresponding form in @var{forms} within @var{body}.
+@code{cl-once-only*} binds @var{variable} to a list of fresh uninterned
+symbols.  @code{cl-once-only*} furthermore wraps the final expansion
+such that each form is evaluated in order and its result is bound to the
+corresponding symbol.
+
+Like @code{cl-once-only}, the first argument can be a symbol
+@var{variable}, which is equivalent to writing @code{(variable
+variable)}.
+
+Consider the following macro:
+
+@example
+(defmacro my-list (head &rest args)
+  (cl-once-only ((args `(list ,@@args))
+    `(list (apply #',head ,args)
+           ,args
+           (nth 1 ,args))))
+@end example
+
+This macro is such that it will evaluate @var{args} only once, however
+that @var{args} was a list is lost once we are in @code{cl-once-only}.
+Furthermore, to access any specific element of @var{args} we must obtain
+the element during evaluation via @code{(nth N ,args)}.
+
+Consider the alternative using @code{cl-once-only*}:
+
+@example
+(defmacro my-list (head &rest args)
+  (cl-once-only* args
+    `(list (,head ,@@args)
+           (list ,@@args)
+           ,(nth 1 args))))
+@end example
+
+which preserves the fact that @var{args} is a list and allows immediate
+access to individual arguments by simply choosing the corresponding
+element in @var{args}.
+@end defmac
+
 @node Macros
 @chapter Macros
 
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 2e501005bf7..adb9cb29104 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -2544,6 +2544,54 @@ cl-once-only
                           collect `(,(car name) ,gensym))
              ,@body)))))
 
+(defmacro cl-once-only* (listvar &rest body)
+  "Generate code to evaluate the list of FORMS just once in BODY.
+
+This is a macro to be used while defining other macros.  FORMS is
+evaluated once during macroexpansion to obtain the list of forms.  In
+the fully expanded code those forms will be evaluated once before BODY
+and their results will be bound to fresh uninterned variables, one for
+each form.
+
+Within the macro VARIABLE is a list of these symbols in order, such that
+each form in FORMS can be accessed by using the corresponding element in
+VARIABLE.
+
+The common case of `(cl-once-only* (VARIABLE VARIABLE) ...)' can be
+written shortly as `(cl-once-only* VARIABLE ...)'.
+
+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.
+
+\(fn (VARIABLE FORMS) &body BODY)"
+  (declare (debug ([&or symbolp (symbolp form)] body)) (indent 1))
+  (let* ((variable (if (symbolp listvar) listvar (nth 0 listvar)))
+         (forms    (if (symbolp listvar) listvar (nth 1 listvar)))
+         (results* (gensym (symbol-name variable))))
+    (cl-once-only (forms)
+      `(let ((,results*
+              (cl-loop for i from 1 to (length ,forms) collect
+                       (make-symbol (format "%s$%d" ',(symbol-name variable) i)))))
+         `(let* ,(cl-mapcar #'list ,results* ,forms)
+            ,(let ((,variable ,results*))
+               ,@body))))))
+
 ;;; Multiple values.
 
 ;;;###autoload
-- 
2.44.2


  reply	other threads:[~2024-08-03 22:40 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-07-28 21:17 bug#72344: [PATCH] Add a version of cl-once-only which handles lists of forms Thuna
2024-07-29  0:39 ` Drew Adams via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-07-29 19:30   ` Thuna
2024-07-29 19:54     ` Drew Adams via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-07-29  7:09 ` Sean Whitton
2024-07-29 19:54   ` Thuna
2024-08-03  2:51     ` Sean Whitton
2024-08-03 22:40       ` Thuna [this message]
2024-08-06  1:41         ` Sean Whitton
2024-08-06  1:47           ` Sean Whitton
2024-08-09  5:44             ` Sean Whitton
2024-08-06 12:28           ` Thuna
2024-08-06 12:37             ` Sean Whitton
2024-08-13 21:17               ` Thuna
2024-08-13 21:36                 ` Drew Adams via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-13 22:18                   ` Thuna
2024-08-13 22:57                     ` Drew Adams via Bug reports for GNU Emacs, the Swiss army knife of text editors
2024-08-14  0:01                     ` Sean Whitton
2024-08-14  0:05                 ` Sean Whitton
2024-08-14  2:21                   ` Thuna
2024-08-14  6:09                     ` Eli Zaretskii
2024-08-14 14:14                       ` Thuna
2024-08-14 14:29                         ` Eli Zaretskii
2024-08-15  1:05                           ` Thuna
2024-08-15  6:28                             ` Eli Zaretskii
2024-08-15 15:15                               ` Thuna
2024-08-15 16:20                                 ` Eli Zaretskii
2024-08-19 16:54                                   ` Richard Stallman
2024-08-15 12:38                         ` Sean Whitton
2024-08-15 15:02                           ` Thuna
2024-08-14  9:47                     ` Sean Whitton
2024-08-06 15:52           ` Drew Adams 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

  List information: https://www.gnu.org/software/emacs/

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

  git send-email \
    --in-reply-to=871q35mdrd.fsf@gmail.com \
    --to=thuna.cing@gmail.com \
    --cc=72344@debbugs.gnu.org \
    --cc=spwhitton@spwhitton.name \
    /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 public inbox

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).