emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

defmacro with built-in gensym declaration and initialization


From: akater
Subject: defmacro with built-in gensym declaration and initialization
Date: Wed, 20 Jan 2021 08:15:46 +0000

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: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]