all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Sean Whitton <spwhitton@spwhitton.name>
To: Eli Zaretskii <eliz@gnu.org>
Cc: emacs-devel@gnu.org
Subject: Re: master 2e9111813b: Add two classic Common Lisp macro-writing macros
Date: Tue, 12 Apr 2022 22:48:42 -0700	[thread overview]
Message-ID: <87k0btpm85.fsf@athena.silentflame.com> (raw)
In-Reply-To: <83k0bu3xzc.fsf@gnu.org>

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

Hello,

Thank you for looking!

On Tue 12 Apr 2022 at 10:25pm +03, Eli Zaretskii wrote:

>> +This macro expands to code that executes @var{body} with each of the
>> +variables in @var{names} bound to a fresh uninterned symbol or
>> +``gensym''.
>
> Instead of "or" I suggest to say "a.k.a.@: @dfn{gensym}" (and add a
> @cindex entry for "gensym", as usual with any term in @dfn).

I've changed this to something else, but I'd prefer not to use an
abbreviation like "a.k.a.".  Thanks for pointing out @dfn.

>> +@defmac cl-once-only ((name form)@dots{}) body@dots{}
>> +This macro is primarily to help the macro programmer ensure that forms
>> +supplied by the user of the macro are evaluated just once by its
>> +expansion even though the result of evaluating the form is to occur
>> +more than once.  Less often, this macro is used to ensure that forms
>> +supplied by the macro programmer are evaluated just once.
>> +
>> +Each @var{name} is a variable which can be used to refer to the result
>> +of evaluating @var{form} in @var{body}.  @code{cl-once-only} binds
>> +each @var{name} to a fresh uninterned symbol during the evaluation of
>> +@var{body}.
>
> My recommendation is always to try to name parameters after their
> roles.  In this case, I'd use VAR or VARIABLE instead of the less
> specific NAME.  Then you could make the text shorter and thus clearer:
>
>   Each @var{variable} can be used to refer to the result of evaluating
>   @var{form} in @var{body}.  @code{cl-once-only} binds each
>   @var{variable} to a fresh uninterned symbol...

Yes, that's better.

> (And why not use "gensym" instead of the wordier "uninterned symbol"?
> that's why you introduced that terminology, right?)

I wanted to use "gensym" in the description of cl-with-gensyms because
of the name of that macro.  But otherwise, I was thinking that it's a
less familiar term in Elisp than it is in CL, and that "uninterned
symbol" was the most familiar Elisp term.  I don't mind changing it if
you think that's not so much of a concern.

>>            Then, @code{cl-once-only} wraps the final expansion in
>> +code to evaluate each @var{form} and bind it to the corresponding
>> +uninterned symbol.
>
> How can a form be bound to a symbol?

In this case it should say that the result of evaluation is bound to the
corresponding uninterned symbol, thanks.

In general, I would describe (let ((x '(+ 1 2))) ...) as a case where a
form is bound to a symbol -- would you describe that differently?

Revised patch attached.

-- 
Sean Whitton

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Document-additions-of-cl-with-gensyms-and-cl-once-on.patch --]
[-- Type: text/x-diff, Size: 5709 bytes --]

From 035d757599c546fffc5acb10a3e692cb2d27d329 Mon Sep 17 00:00:00 2001
From: Sean Whitton <spwhitton@spwhitton.name>
Date: Tue, 12 Apr 2022 11:38:32 -0700
Subject: [PATCH] Document additions of cl-with-gensyms and cl-once-only

* NEWS: Document additions of cl-with-gensyms and cl-once-only.
* doc/misc/cl.texi (Macro-Writing Macros): New section.
(Creating Symbols): Add to the concept index under the name "gensym".
(Obsolete Setf Customization): Use cl-once-only rather than
macroexp-let2, and fix a quotation bug in one example.
---
 doc/misc/cl.texi | 86 ++++++++++++++++++++++++++++++++++++++++++++++--
 etc/NEWS         |  4 +++
 2 files changed, 88 insertions(+), 2 deletions(-)

diff --git a/doc/misc/cl.texi b/doc/misc/cl.texi
index a6fe29e102..0946510882 100644
--- a/doc/misc/cl.texi
+++ b/doc/misc/cl.texi
@@ -843,6 +843,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}.
 @end menu
 
 @node Assignment
@@ -2513,6 +2514,86 @@ Multiple Values
 Since a perfect emulation is not feasible in Emacs Lisp, this
 package opts to keep it as simple and predictable as possible.
 
+@node Macro-Writing Macros
+@section Macro-Writing Macros
+
+@noindent
+This package includes two classic Common Lisp macro-writing macros to
+help render complex macrology easier to read.
+
+@defmac cl-with-gensyms names@dots{} body
+This macro expands to code that executes @var{body} with each of the
+variables in @var{names} bound to a fresh uninterned symbol, or
+@dfn{gensym}, in Common Lisp parlance.  For macros requiring more than
+one gensym, use of @code{cl-with-gensyms} shortens the code and
+renders one's intentions clearer.  Compare:
+
+@example
+(defmacro my-macro (foo)
+  (let ((bar (gensym "bar"))
+        (baz (gensym "baz"))
+        (quux (gensym "quux")))
+    `(let ((,bar (+ @dots{})))
+       @dots{})))
+
+(defmacro my-macro (foo)
+  (cl-with-gensyms (bar baz quux)
+    `(let ((,bar (+ @dots{})))
+       @dots{})))
+@end example
+@end defmac
+
+@defmac cl-once-only ((variable form)@dots{}) body
+This macro is primarily to help the macro programmer ensure that forms
+supplied by the user of the macro are evaluated just once by its
+expansion even though the result of evaluating the form is to occur
+more than once.  Less often, this macro is used to ensure that forms
+supplied by the macro programmer are evaluated just once.
+
+Each @var{variable} which can be used to refer to the result of
+evaluating @var{form} in @var{body}.  @code{cl-once-only} binds each
+@var{variable} to a fresh uninterned symbol during the evaluation of
+@var{body}.  Then, @code{cl-once-only} wraps the final expansion in
+code to evaluate each @var{form} and bind the result to the
+corresponding uninterned symbol.  Thus, when the macro writer
+substitutes the value for @var{variable} into the expansion they are
+effectively referring to the result of evaluating @var{form}, rather
+than @var{form} itself.  Another way to put this is that each
+@var{variable} is bound to an expression for the (singular) result of
+evaluating @var{form}.
+
+The most common case is where @var{variable} is one of the arguments
+to the macro being written, so @code{(variable variable)} may be
+abbreviated to just @code{variable}.
+
+For example, consider this macro:
+
+@example
+(defmacro my-list (x y &rest forms)
+  (let ((x-result (gensym))
+        (y-result (gensym)))
+    `(let ((,x-result ,x)
+           (,y-result ,y))
+       (list ,x-result ,y-result ,x-result ,y-result
+             (progn ,@@forms))))
+@end example
+
+In a call like @w{@code{(my-list (pop foo) @dots{})}} the intermediate
+binding to @code{x-result} ensures that the @code{pop} is not done
+twice.  But as a result the code is rather complex: the reader must
+keep track of how @code{x-result} really just means the first
+parameter of the call to the macro, and the required use of multiple
+gensyms to avoid variable capture by @code{(progn ,@@forms)} obscures
+things further.  @code{cl-once-only} takes care of these details:
+
+@example
+(defmacro my-list (x &rest forms)
+  (cl-once-only (x y)
+    `(list ,x ,y ,x ,y
+           (progn ,@@forms))))
+@end example
+@end defmac
+
 @node Macros
 @chapter Macros
 
@@ -2868,6 +2949,7 @@ Property Lists
 
 @node Creating Symbols
 @section Creating Symbols
+@cindex gensym
 
 @noindent
 These functions create unique symbols, typically for use as
@@ -5028,13 +5110,13 @@ Obsolete Setf Customization
 @example
 (defmacro incf (place &optional n)
   (gv-letplace (getter setter) place
-    (macroexp-let2 nil v (or n 1)
+    (cl-once-only ((v (or n 1)))
       (funcall setter `(+ ,v ,getter)))))
 @end example
 @ignore
 (defmacro concatf (place &rest args)
   (gv-letplace (getter setter) place
-    (macroexp-let2 nil v (mapconcat 'identity args)
+    (cl-once-only ((v `(mapconcat 'identity ',args)))
       (funcall setter `(concat ,getter ,v)))))
 @end ignore
 @end defmac
diff --git a/etc/NEWS b/etc/NEWS
index 79c27da549..24e45b0483 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1363,6 +1363,10 @@ functions.
 +++
 ** 'macroexp-let2*' can omit 'test' arg and use single-var bindings.
 
++++
+** New macro-writing macros, 'cl-with-gensyms' and 'cl-once-only'.
+See the '(cl) Macro-Writing Macros' manual section for descriptions.
+
 +++
 ** New variable 'last-event-device' and new function 'device-class'.
 On X Windows, 'last-event-device' specifies the input extension device
-- 
2.30.2


  reply	other threads:[~2022-04-13  5:48 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <164974332528.14217.12591424007013368601@vcs2.savannah.gnu.org>
     [not found] ` <20220412060205.8B446C01687@vcs2.savannah.gnu.org>
2022-04-12  6:15   ` master 2e9111813b: Add two classic Common Lisp macro-writing macros Sean Whitton
2022-04-12  7:14     ` Stefan Monnier
2022-04-12 15:57       ` Sean Whitton
2022-04-12 16:22         ` Stefan Monnier
2022-04-12  6:42   ` Po Lu
2022-04-12 18:43     ` Sean Whitton
2022-04-12 19:25       ` Eli Zaretskii
2022-04-13  5:48         ` Sean Whitton [this message]
2022-04-13 12:35           ` Eli Zaretskii
2022-04-13 19:46           ` Johann Klähn
2022-04-13 23:16             ` Sean Whitton
2022-04-13  3:58     ` Richard Stallman
2022-04-13  5:08       ` Sean Whitton
2022-04-14  2:56         ` Richard Stallman
2022-04-14  5:14           ` Sean Whitton
2022-04-14 12:06             ` [External] : " Drew Adams
2022-04-15  3:59             ` Richard Stallman
2022-04-15  6:14               ` Eli Zaretskii
2022-04-17  4:09                 ` Richard Stallman
2022-04-17  1:57               ` Michael Heerdegen
2022-04-18  2:41                 ` Richard Stallman
2022-04-18  5:18                   ` Eli Zaretskii
2022-04-19  3:49                     ` Richard Stallman
2022-04-12  8:46   ` Lars Ingebrigtsen
2022-04-12 15:52     ` Sean Whitton
2022-04-12 15:59       ` Lars Ingebrigtsen
2022-04-13  9:21   ` Philip Kaludercic
2022-04-13 15:14     ` Stefan Monnier

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=87k0btpm85.fsf@athena.silentflame.com \
    --to=spwhitton@spwhitton.name \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    /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.