Skip to content

Replace with-plist-vars #7

@Luis-Henriquez-Perez

Description

@Luis-Henriquez-Perez

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions