unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
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 --]

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