unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* [PATCH] Adding with-gensyms and once-only to subr-x
@ 2022-04-11 16:26 Sean Whitton
  2022-04-11 16:48 ` Stefan Monnier
  0 siblings, 1 reply; 13+ messages in thread
From: Sean Whitton @ 2022-04-11 16:26 UTC (permalink / raw)
  To: emacs-devel

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

Hello,

Lately I have been finding myself wanting with-gensyms and once-only,
two classic macro-writing macros, available in core Elisp.  (There is
already org-with-gensyms, but it's not in the least bit Org-specific.)

Here is my implementation for review.

-- 
Sean Whitton

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-lisp-emacs-lisp-subr-x.el-with-gensyms-once-only-New.patch --]
[-- Type: text/x-diff, Size: 2430 bytes --]

From 89e86db1ccb97ce3f91d5b4beb5d7b461311d196 Mon Sep 17 00:00:00 2001
From: Sean Whitton <spwhitton@spwhitton.name>
Date: Mon, 11 Apr 2022 09:20:35 -0700
Subject: [PATCH] * lisp/emacs-lisp/subr-x.el (with-gensyms, once-only): New
 macros.

---
 lisp/emacs-lisp/subr-x.el | 43 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el
index abf85ab6c6..c74bf7f5f0 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -526,6 +526,49 @@ read-process-name
         (error "No process selected"))
       process)))
 
+(defmacro with-gensyms (names &rest body)
+  "Bind each of NAMES to an uninterned symbol and evaluate BODY."
+  (declare (debug (sexp body)) (indent 1))
+  `(let ,(cl-loop for name in names collect
+                  `(,name (gensym (symbol-name ',name))))
+     ,@body))
+
+(defmacro once-only (names &rest body)
+  "Generate code to evaluate each of NAMES just once in BODY.
+
+This macro helps with writing other macros.  Each of names is
+either (NAME FORM) or NAME, which latter means (NAME NAME).
+During macroexpansion, each NAME is bound to an uninterned
+symbol.  The expansion evaluates each FORM and binds it to the
+corresponding uninterned symbol.
+
+For example, consider this macro:
+
+    (defmacro my-cons (x)
+      (once-only (x)
+        `(cons ,x ,x)))
+
+Consider the call (my-cons (pop y)).  This will expand to
+something like this:
+
+    (let ((g1 (pop y)))
+      (cons g1 g1))
+
+This ensures that the pop is performed only once, as wanted."
+  (declare (debug (sexp body)) (indent 1))
+  (setq names (mapcar #'ensure-list names))
+  (let ((our-gensyms (cl-loop for name in names collect (gensym))))
+    ;; During macroexpansion, obtain a gensym for each NAME.
+    `(let ,(cl-loop for sym in our-gensyms collect `(,sym (gensym)))
+       ;; Evaluate each FORM and bind to the corresponding gensym.
+       `(let (,,@(cl-loop for name in names and gensym in our-gensyms
+                          for to-eval = (or (cadr name) (car name))
+                          collect ``(,,gensym ,,to-eval)))
+          ;; During macroexpansion, bind each NAME to its gensym.
+          ,(let ,(cl-loop for name in names and gensym in our-gensyms
+                          collect `(,(car name) ,gensym))
+             ,@body)))))
+
 (provide 'subr-x)
 
 ;;; subr-x.el ends here
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-11 16:26 [PATCH] Adding with-gensyms and once-only to subr-x Sean Whitton
@ 2022-04-11 16:48 ` Stefan Monnier
  2022-04-11 17:01   ` Sean Whitton
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2022-04-11 16:48 UTC (permalink / raw)
  To: Sean Whitton; +Cc: emacs-devel

> Lately I have been finding myself wanting with-gensyms and once-only,
> two classic macro-writing macros, available in core Elisp.  (There is
> already org-with-gensyms, but it's not in the least bit Org-specific.)

I think `once-only` is what we (well, I) called `macroexp-let2`.


        Stefan




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-11 16:48 ` Stefan Monnier
@ 2022-04-11 17:01   ` Sean Whitton
  2022-04-11 17:26     ` Stefan Monnier
  0 siblings, 1 reply; 13+ messages in thread
From: Sean Whitton @ 2022-04-11 17:01 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello,

On Mon 11 Apr 2022 at 12:48pm -04, Stefan Monnier wrote:

>> Lately I have been finding myself wanting with-gensyms and once-only,
>> two classic macro-writing macros, available in core Elisp.  (There is
>> already org-with-gensyms, but it's not in the least bit Org-specific.)
>
> I think `once-only` is what we (well, I) called `macroexp-let2`.

Ah, thank you for the pointer.

(macroexp-let2* nil ((foo foo) (bar bar)) FORMS)

is quite a bit more verbose than (once-only (foo bar) FORMS), however.
So I would be inclined to keep once-only, though reimplemented in terms
of macroexp-let2*.  Does that sound reasonable?

-- 
Sean Whitton



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-11 17:01   ` Sean Whitton
@ 2022-04-11 17:26     ` Stefan Monnier
  2022-04-11 18:41       ` Sean Whitton
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2022-04-11 17:26 UTC (permalink / raw)
  To: Sean Whitton; +Cc: emacs-devel

>>> Lately I have been finding myself wanting with-gensyms and once-only,
>>> two classic macro-writing macros, available in core Elisp.  (There is
>>> already org-with-gensyms, but it's not in the least bit Org-specific.)
>> I think `once-only` is what we (well, I) called `macroexp-let2`.
> Ah, thank you for the pointer.
>
> (macroexp-let2* nil ((foo foo) (bar bar)) FORMS)
>
> is quite a bit more verbose than (once-only (foo bar) FORMS), however.
> So I would be inclined to keep once-only, though reimplemented in terms
> of macroexp-let2*.  Does that sound reasonable?

How 'bout making `macroexp-let2*` accept the shorter form as in the
patch below?

I'm not in love with the name `macroexp-let2*` but I find the name
`once-only` to lack context.  So we could consider renaming, but I'd
favor making it live in `macroexp.el` (and come with a `macroexp-`
prefix).  It could be called `macroexp-once-only` or
`macroexp-eval-now`, or we could go crazy with names like
`macroexp-copyableize`.


        Stefan


diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el
index 1420655f1e9..4783e55e057 100644
--- a/lisp/emacs-lisp/macroexp.el
+++ b/lisp/emacs-lisp/macroexp.el
@@ -636,12 +636,20 @@ macroexp-let2
 (defmacro macroexp-let2* (test bindings &rest body)
   "Multiple binding version of `macroexp-let2'.
 
-BINDINGS is a list of elements of the form (SYM EXP).  Each EXP
-can refer to symbols specified earlier in the binding list."
+BINDINGS is a list of elements of the form (SYM EXP) or just SYM,
+which then stands for (SYM SYM).
+Each EXP can refer to symbols specified earlier in the binding list.
+
+TEST has to be a symbol, and if it is nil it can be omitted."
   (declare (indent 2) (debug (sexp (&rest (sexp form)) body)))
+  (when (consp test) ;; `test' was omitted.
+    (push bindings body)
+    (setq bindings test)
+    (setq test nil))
   (pcase-exhaustive bindings
     ('nil (macroexp-progn body))
-    (`((,var ,exp) . ,tl)
+    (`(,(or `(,var ,exp) (and (pred symbolp) ,var (let ,exp var)))
+       . ,tl)
      `(macroexp-let2 ,test ,var ,exp
         (macroexp-let2* ,test ,tl ,@body)))))
 




^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-11 17:26     ` Stefan Monnier
@ 2022-04-11 18:41       ` Sean Whitton
  2022-04-11 19:11         ` Stefan Monnier
  0 siblings, 1 reply; 13+ messages in thread
From: Sean Whitton @ 2022-04-11 18:41 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello,

On Mon 11 Apr 2022 at 01:26PM -04, Stefan Monnier wrote:

> How 'bout making `macroexp-let2*` accept the shorter form as in the
> patch below?

Nice.  Good with me.  (I thought about extending macroexp-let2* but
didn't think about the idea of making the first argument optional.)

> I'm not in love with the name `macroexp-let2*` but I find the name
> `once-only` to lack context.  So we could consider renaming, but I'd
> favor making it live in `macroexp.el` (and come with a `macroexp-`
> prefix).  It could be called `macroexp-once-only` or
> `macroexp-eval-now`, or we could go crazy with names like
> `macroexp-copyableize`.

How about I add an alias cl-once-only, maybe which doesn't accept the
TEST argument at all?  It can go in cl-macs.el.

'once-only' is the very well-established CL name for this macro,
referenced in all of "Practical Common Lisp", "Paradigms of AI
Programming", "Let Over Lambda" and I think "On Lisp" too.  Speaking as
someone who has written more CL than Elisp, I would find it helpful to
have it under that name, and I should think others would too.

-- 
Sean Whitton



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-11 18:41       ` Sean Whitton
@ 2022-04-11 19:11         ` Stefan Monnier
  2022-04-11 20:25           ` Sean Whitton
  2022-04-12  0:06           ` Sean Whitton
  0 siblings, 2 replies; 13+ messages in thread
From: Stefan Monnier @ 2022-04-11 19:11 UTC (permalink / raw)
  To: Sean Whitton; +Cc: emacs-devel

>> How 'bout making `macroexp-let2*` accept the shorter form as in the
>> patch below?
> Nice.  Good with me.  (I thought about extending macroexp-let2* but
> didn't think about the idea of making the first argument optional.)

Pushed to `master`.

>> I'm not in love with the name `macroexp-let2*` but I find the name
>> `once-only` to lack context.  So we could consider renaming, but I'd
>> favor making it live in `macroexp.el` (and come with a `macroexp-`
>> prefix).  It could be called `macroexp-once-only` or
>> `macroexp-eval-now`, or we could go crazy with names like
>> `macroexp-copyableize`.
>
> How about I add an alias cl-once-only, maybe which doesn't accept the
> TEST argument at all?  It can go in cl-macs.el.

Deal!
[ I didn't know it was "standard" in Common-Lisp.  ]


        Stefan




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-11 19:11         ` Stefan Monnier
@ 2022-04-11 20:25           ` Sean Whitton
  2022-04-11 21:11             ` Stefan Monnier
  2022-04-12  0:06           ` Sean Whitton
  1 sibling, 1 reply; 13+ messages in thread
From: Sean Whitton @ 2022-04-11 20:25 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello,

On Mon 11 Apr 2022 at 03:11PM -04, Stefan Monnier wrote:

>>> How 'bout making `macroexp-let2*` accept the shorter form as in the
>>> patch below?
>> Nice.  Good with me.  (I thought about extending macroexp-let2* but
>> didn't think about the idea of making the first argument optional.)
>
> Pushed to `master`.

Cool, I'll add the cl-once-only alias shortly.

Are you okay with 'with-gensyms' in subr-x?  Another option is to call
it macroexp-with-gensyms and add a cl-with-gensyms alias, or add
cl-with-gensyms alone (the name is similarly standard).  But as the
macro is much simpler and more commonly used, it would be nice to have
the shorter name.

-- 
Sean Whitton



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-11 20:25           ` Sean Whitton
@ 2022-04-11 21:11             ` Stefan Monnier
  2022-04-11 23:05               ` Sean Whitton
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2022-04-11 21:11 UTC (permalink / raw)
  To: Sean Whitton; +Cc: emacs-devel

> Are you okay with 'with-gensyms' in subr-x?  Another option is to call
> it macroexp-with-gensyms and add a cl-with-gensyms alias, or add
> cl-with-gensyms alone (the name is similarly standard).  But as the
> macro is much simpler and more commonly used, it would be nice to have
> the shorter name.

`cl-with-gensyms` is fine with me.


        Stefan




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-11 21:11             ` Stefan Monnier
@ 2022-04-11 23:05               ` Sean Whitton
  2022-04-11 23:15                 ` Sean Whitton
  0 siblings, 1 reply; 13+ messages in thread
From: Sean Whitton @ 2022-04-11 23:05 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello,

On Mon 11 Apr 2022 at 05:11PM -04, Stefan Monnier wrote:

>> Are you okay with 'with-gensyms' in subr-x?  Another option is to call
>> it macroexp-with-gensyms and add a cl-with-gensyms alias, or add
>> cl-with-gensyms alone (the name is similarly standard).  But as the
>> macro is much simpler and more commonly used, it would be nice to have
>> the shorter name.
>
> `cl-with-gensyms` is fine with me.

Sorry, so you mean adding cl-with-gensyms to cl-macs.el rather than
adding with-gensyms to subr-x as in my original patch?

-- 
Sean Whitton



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-11 23:05               ` Sean Whitton
@ 2022-04-11 23:15                 ` Sean Whitton
  0 siblings, 0 replies; 13+ messages in thread
From: Sean Whitton @ 2022-04-11 23:15 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello,

On Mon 11 Apr 2022 at 04:05PM -07, Sean Whitton wrote:

> Hello,
>
> On Mon 11 Apr 2022 at 05:11PM -04, Stefan Monnier wrote:
>
>>> Are you okay with 'with-gensyms' in subr-x?  Another option is to call
>>> it macroexp-with-gensyms and add a cl-with-gensyms alias, or add
>>> cl-with-gensyms alone (the name is similarly standard).  But as the
>>> macro is much simpler and more commonly used, it would be nice to have
>>> the shorter name.
>>
>> `cl-with-gensyms` is fine with me.
>
> Sorry, so you mean adding cl-with-gensyms to cl-macs.el rather than
> adding with-gensyms to subr-x as in my original patch?

On second thoughts, cl-with-gensyms is probably easier to remember than
with-gensyms, given that we are going with cl-once-only.  I'll push
that shortly.  Thanks again.

-- 
Sean Whitton



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-11 19:11         ` Stefan Monnier
  2022-04-11 20:25           ` Sean Whitton
@ 2022-04-12  0:06           ` Sean Whitton
  2022-04-12  3:08             ` Stefan Monnier
  1 sibling, 1 reply; 13+ messages in thread
From: Sean Whitton @ 2022-04-12  0:06 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello again Stefan,

On Mon 11 Apr 2022 at 03:11PM -04, Stefan Monnier wrote:

>>> I'm not in love with the name `macroexp-let2*` but I find the name
>>> `once-only` to lack context.  So we could consider renaming, but I'd
>>> favor making it live in `macroexp.el` (and come with a `macroexp-`
>>> prefix).  It could be called `macroexp-once-only` or
>>> `macroexp-eval-now`, or we could go crazy with names like
>>> `macroexp-copyableize`.
>>
>> How about I add an alias cl-once-only, maybe which doesn't accept the
>> TEST argument at all?  It can go in cl-macs.el.
>
> Deal!
> [ I didn't know it was "standard" in Common-Lisp.  ]

I've found a snag.  CL once-only uses let but macroexp-let2* uses let*.

For the example

    (defmacro square2 (x y)
      (cl-once-only (x y)
        `(* ,x ,x ,y ,y)))

the macroexp-let2* version yields something like

    (let* ((x foo)
           (y bar))
      (* x x y y))

whereas CL's traditional once-only would be more like

    (let ((x foo)
          (y bar))
      (* x x y y))

where x and y are uninterned symbols.

At the very least this should be noted in the docstring for
cl-once-only, but maybe it means cl-once-only should use the code in my
original patch and not use macroexp-let2* at all?  What do you think?

-- 
Sean Whitton



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-12  0:06           ` Sean Whitton
@ 2022-04-12  3:08             ` Stefan Monnier
  2022-04-12  6:02               ` Sean Whitton
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2022-04-12  3:08 UTC (permalink / raw)
  To: Sean Whitton; +Cc: emacs-devel

> At the very least this should be noted in the docstring for
> cl-once-only, but maybe it means cl-once-only should use the code in my
> original patch and not use macroexp-let2* at all?  What do you think?

It's not very important that it use `macroexp-let2*` internally.


        Stefan




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Adding with-gensyms and once-only to subr-x
  2022-04-12  3:08             ` Stefan Monnier
@ 2022-04-12  6:02               ` Sean Whitton
  0 siblings, 0 replies; 13+ messages in thread
From: Sean Whitton @ 2022-04-12  6:02 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

Hello,

On Mon 11 Apr 2022 at 11:08PM -04, Stefan Monnier wrote:

>> At the very least this should be noted in the docstring for
>> cl-once-only, but maybe it means cl-once-only should use the code in my
>> original patch and not use macroexp-let2* at all?  What do you think?
>
> It's not very important that it use `macroexp-let2*` internally.

Cool, pushed, thanks again!

-- 
Sean Whitton



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2022-04-12  6:02 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-11 16:26 [PATCH] Adding with-gensyms and once-only to subr-x Sean Whitton
2022-04-11 16:48 ` Stefan Monnier
2022-04-11 17:01   ` Sean Whitton
2022-04-11 17:26     ` Stefan Monnier
2022-04-11 18:41       ` Sean Whitton
2022-04-11 19:11         ` Stefan Monnier
2022-04-11 20:25           ` Sean Whitton
2022-04-11 21:11             ` Stefan Monnier
2022-04-11 23:05               ` Sean Whitton
2022-04-11 23:15                 ` Sean Whitton
2022-04-12  0:06           ` Sean Whitton
2022-04-12  3:08             ` Stefan Monnier
2022-04-12  6:02               ` Sean Whitton

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).