-
Notifications
You must be signed in to change notification settings - Fork 48
Description
First, thanks for this handbook. I haven't developed any emacs packages (yet), but even for my own init.el customizations this has been invaluable.
I was using the with-plist-vars
macro and I liked it. However, I found that it had some significant shortcomings.
One is that it requires a raw plist and will not work with a variable which contains a plist.
For instance (progn (setq plist '(:hi 1 :ho 2)) (with-plist-vars plist (+ hi ho))
does not work.
Equally problematic was the fact that it used variables with the same name as
the keys. These variables are likely to clobber other variables because they're
not prefixed by anything. Additionally, often times I want to try to get a value
from a key that I don't know is in the plist. Since there is no special notation
between a variable that refers to a key and any other variable I
have no way of doing this.
I would recommend adding this macro instead to your handbook:
(defmacro let-plist (plist &rest body)
"Let-bind dotted symbols to their value in PLIST and execute BODY.
Dotted symbol is any symbol starting with a .. Only those present
in BODY are let-bound and this search is done at compile time.
For instance, the following code
(let-plist plist
(if (and .title .body)
.body
.site))
essentially expands to
(let ((.title (plist-get plist :title))
(.body (plist-get plist :body))
(.site (plist-get plist :site)))
(if (and .title .body)
.body
.site))
"
(declare (indent defun))
(require 'let-alist)
(let ((plist-var (gensym)))
`(let*
((,plist-var ,(if (and (listp plist) (not (eq (car plist) 'quote)))
`(quote ,plist)
plist))
,@(cl-loop for (dotvar . var) in (let-alist--deep-dot-search body)
for key = (intern (concat ":" (symbol-name var)))
collect `(,dotvar (plist-get ,plist-var ,key))))
,@body)))
This macro addresses both these misgivings and is consistent with the let-alist
macro already built-in to 26.1. It uses let-alist--deep-dot-search-body
to get the variables which are prefixed by a .
.
EDIT: fixed leak with plist
var. Before the edit, if you passed in some expression which would evaluate to a plist, the expression would be evaluated one time for every unique dot variable. If the expression had side-effects or if it was computationally expensive, this could lead to unexpected results. Also I check to see if plist is an unquoted list and if it is I quote it. This is for convenience so that (let-plist (:hi 1 :ho 2) ...)
will work even though the raw plist isn't quoted.
EDIT: Add (require 'let-alist)
.