From: akater <nuclearspace@gmail.com>
To: emacs-devel@gnu.org
Subject: defmacro with built-in gensym declaration and initialization
Date: Wed, 20 Jan 2021 08:15:46 +0000 [thread overview]
Message-ID: <87bldk6n2l.fsf@gmail.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 4223 bytes --]
I suggest extending ~defmacro~ to support ~&gensym~ keyword in its
lambda list, for convenient declaration and initialization of gensyms.
Below there's a link to working implementation, and to specification.
A realistic example from popular package =dash=
#+begin_example emacs-lisp
(defmacro --partition-by (form list)
"Anaphoric form of `-partition-by'."
(declare (debug (form form)))
(let ((r (make-symbol "result"))
(s (make-symbol "sublist"))
(v (make-symbol "value"))
(n (make-symbol "new-value"))
(l (make-symbol "list")))
`(let ((,l ,list))
(when ,l
(let* ((,r nil)
(it (car ,l))
(,s (list it))
(,v ,form)
(,l (cdr ,l)))
(while ,l
(let* ((it (car ,l))
(,n ,form))
(unless (equal ,v ,n)
(!cons (nreverse ,s) ,r)
(setq ,s nil)
(setq ,v ,n))
(!cons it ,s)
(!cdr ,l)))
(!cons (nreverse ,s) ,r)
(nreverse ,r))))))
#+end_example
could then be rewritten as
#+begin_example emacs-lisp
(defmacro --partition-by ( form list
&gensym result sublist value new-value list)
"Anaphoric form of `-partition-by'."
(declare (debug (form form)))
`(when ,list
(let* ((,result nil)
(it (car ,list))
(,sublist (list it))
(,value ,form)
(,list (cdr ,list)))
(while ,list
(let* ((it (car ,list))
(,new-value ,form))
(unless (equal ,value ,new-value)
(!cons (nreverse ,sublist) ,result)
(setq ,sublist nil)
(setq ,value ,new-value))
(!cons it ,sublist)
(!cdr ,list)))
(!cons (nreverse ,sublist) ,result)
(nreverse ,result))))
#+end_example
Lambda list keyword ~&gensym~, as implemented below, also provides
~once-only~ functionality. For example, the definition
#+begin_example emacs-lisp
(defmacro with-file-buffer (filename &rest body)
"Visit FILENAME unless already visited. Set the buffer as current,
evaluate BODY forms. Kill the buffer if it did not exist initially."
(declare (indent 1))
(let ((o-o-filename (gensym "filename-"))
(exists-g (gensym "exists-"))
(buffer-g (gensym "buffer-")))
`(let* ((,o-o-filename ,filename)
(,exists-g (get-file-buffer ,o-o-filename))
(,buffer-g (or ,exists-g (find-file-noselect ,o-o-filename))))
(unwind-protect (with-current-buffer ,buffer-g ,@body)
(unless ,exists-g (kill-buffer ,buffer-g))))))
#+end_example
could be rewritten as
#+begin_example emacs-lisp
(defmacro with-file-buffer ( filename &rest body
&gensym
filename
(exists (get-file-buffer filename))
(buffer (or exists
(find-file-noselect filename))))
"Visit FILENAME unless already visited. Set the buffer as current,
evaluate BODY forms. Kill the buffer if it did not exist initially."
(declare (indent 1))
`(unwind-protect (with-current-buffer ,buffer ,@body)
(unless ,exists (kill-buffer ,buffer))))
#+end_example
This ~&gensym~ facility eliminates the need for ~with-gensyms~ and
~once-only~ in cases when gensyms are created unconditionally by macro
function (in Common Lisp parlance).
An implementation ~defmacro/&gensym~ is accessible at
git@gitlab.com:akater/defmacro-gensym.git
It is a fairly lengthy (26.6k words) Org file with exposition,
motivation, somewhat extensive tests (for the macro, its variations
and essential dependencies), examples, a ~cl-demacro/&gensym~ version
that supports destructuring lambda lists, and variations which expand
constants early. There are 118 tests and examples for all the
variations in total. The file can be tangled to a self-contained
elisp file that provides the macros in question.
Patch not provided because my implementation depends significantly on
~cl-symbol-macrolet~ which doesn't look like it could be used in such
a low-level code.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 865 bytes --]
next reply other threads:[~2021-01-20 8:15 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-01-20 8:15 akater [this message]
2021-01-20 13:46 ` defmacro with built-in gensym declaration and initialization Basil L. Contovounesios
2021-01-20 15:09 ` Stefan Monnier
2021-01-20 16:47 ` Robin Tarsiger
2021-01-20 19:28 ` akater
2021-01-20 20:55 ` Basil L. Contovounesios
2021-01-21 19:34 ` akater
2021-01-21 20:50 ` Stefan Monnier
2021-01-21 20:50 ` Basil L. Contovounesios
2021-01-21 21:05 ` Stefan Monnier
2021-01-21 21:23 ` Basil L. Contovounesios
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=87bldk6n2l.fsf@gmail.com \
--to=nuclearspace@gmail.com \
--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 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).