unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* defmacro with built-in gensym declaration and initialization
@ 2021-01-20  8:15 akater
  2021-01-20 13:46 ` Basil L. Contovounesios
  0 siblings, 1 reply; 11+ messages in thread
From: akater @ 2021-01-20  8:15 UTC (permalink / raw)
  To: emacs-devel

[-- 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 --]

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

end of thread, other threads:[~2021-01-21 21:23 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-20  8:15 defmacro with built-in gensym declaration and initialization akater
2021-01-20 13:46 ` 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

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