Discover Destructuring Assignment in Elisp

Discover Destructuring Assignment in Elisp

LISt Processing in Emacs Lisp obviously involves a lot of juggling with lists and their elements. What else would be more convenient than generalizing the access and binding of list elements? Not only does the concept of destructuring assignment come along with code that is easier to write but also easier to read (terse, patterns that visually cue what elements are supposed to be assigned to variables). Alas, some features of Elisp have to be discovered. While pcase got its node in the Elisp Manual, neither is there an explanation what QPATTERN and UPATTERN mean nor are the related macros ever even mentioned. As if this wasn’t enough, the docstrings of pcase-let and its starred equivalent will leave the average Emacs user puzzled, pcase-dolist doesn’t even have one. This will hopefully change in subsequent versions of Emacs. For now, get ready to embark on a journey of discovery!

pcase is by far the most frequently used macro from pcase.el. What it does is pattern matching, a concept that goes beyond the scope of a blogpost. If you’re familiar with the Fibonacci Sequence, the following example is self-explanatory:

(defun fib (n)
  (pcase n
    (`0 1)
    (`1 1)
    (n (+ (fib (- n 1)) (fib (- n 2))))))
(mapcar 'fib (number-sequence 0 6))
(1 1 2 3 5 8 13)

Generally, pcase is used as a powerful conditional programming construct. Several examples can be found on this EmacsWiki page. Especially suited to the beforementioned destructuring is pcase-let:

    ((`(,spec ,month ,day ,name) (nth 3 holiday-general-holidays)))
  (princ (format "%s is on 2016-%d-%d" name month day))
"Valentine's Day is on 2016-2-14"

The practical advantage will become patently obvious when trying to do the same with let:

(let* ((l (nth 3 holiday-general-holidays))
       (spec (car l))
       (month (cadr l))
       (day (caddr l))
       (name (cadddr l)))
  (princ (format "%s is on 2016-%d-%d" name month day))))

pcase-let in its simplest form resembles Python’s poor man’s destructuring-bind, called tuple and list unpacking:

([a, b, c], d, e) = ([1, 1, 2], 3, 5)
print a + b if a < d else d

Probably even more interesting is pcase-dolist that iterates over the lists of a list:

(let ((l '()))
  (pcase-dolist (`(,spec ,month ,day . ,rest) holiday-general-holidays)
    (push (cons month day
                (if (stringp (car rest)) rest (cdr rest)))
  (nreverse l))
Table 1: Holidays common throughout the United States
Month Holiday
1 New Year's Day
1 Martin Luther King Day
2 Groundhog Day
2 Valentine's Day
2 President's Day
3 St. Patrick's Day
4 April Fools' Day
5 Mother's Day
5 Memorial Day
6 Flag Day
6 Father's Day
7 Independence Day
9 Labor Day
10 Columbus Day
10 Halloween
11 Veteran's Day
11 Thanksgiving

Digging even further into the library, you’ll discover a pcase-lambda. Yet, I’m still not sure what it does besides accepting pcase patterns. But I won’t worry for now, there is exactly ONE appearance of pcase-lambda in the Emacs sources.