Further Into Emacs with Pcase, Backquotes, and Commas

The Continuing Adventures of Writing to Learn

This post is a callback to Alex Schroeder: 2021-09-17 Writing to learn; to remember that writing is one method of explaining something. In this case, I’m explaining to myself what I’ve learned.

I submitted the following issue for org-roam: Allow for `org-roam-buffer`’s Backlinks section to be unique per source. On I submitted a patch to address the issue: Adding unique option fro org-roam-backlinks-section.

While chatting with Jethro Org Roam’s maintainer he suggested using a pcase construct. I have read the pcase documentation and struggled to sift through it. It’s right on the boundary of my comprehension. So I proceeded with my pull request.

Later, I submitted a proposal for a customization, and Jethro explained that the pcase construct would likely be cleaner and more generalizable. He then wrote up that change and pinged me. Thank you Jethro, now I have a pcase use case that I understand what we’re doing, which will help me move pcase further into my area of comprehension.

To learn, I’m going to write about the change that Jethro put forward:

(dolist (section-fn org-roam-mode-section-functions)
  (pcase section-fn
    ((pred functionp)
     (funcall section-fn org-roam-buffer-current-node))
    (`(,fn . ,args)
     (apply fn (cons org-roam-buffer-current-node args)))
     (user-error "Invalid `org-roam-mode-section-functions specification.'")))))

As I’m reading through the code, I also have on hand the pcase manual section and the backquote manual section.

Line 1: This iterates over the org-roam-mode-section-functions list. Each element of the list is section-fn. The element is “passed” to the “anonymous function” that is lines 2 through 8 (e.g. the “body” of the dolist).

Line 2: The pcase expression we’re evaluating is the section-fn. Reading the docstring for pcase, it doesn’t say mention it explicitly, but the verbose name for pcase could be patterning-matching-case-statement.

Line 3: This is the first pattern that we check. This line answers the question: Is the section-fn a function?

Line 4: When section-fn is a function, call that function passing the org-roam-buffer-current-node as the only argument.

Line 5: This is the line that breaks me. What I do know is that when section-fn is (org-roam-backlinks-section :unique t) then this is a match. But

Line 6: Call the fn (which is declared in line 5?) with the org-roam-buffer-current-node and the args. Okay, this is breaking my brain a bit.

Line 7 and 8: The fallback is to raise a user-error.

That Which Breaks Me

On a cursory read, line 5 and 6 confound me. My mind wonders what is fn and args? How do they become the “variables” of line 6?

While thrashing against this, I started building up some search terms: “pcase emacs backquote”. Which lead me to Backquote-Style Patterns. Jackpot!

Backquote-style patterns are a powerful set of pcase pattern extensions (created using pcase-defmacro) that make it easy to match expval against specifications of its structure.

Of course, it’s in the manual. And reading further, I see the following:

The first three clauses use backquote-style patterns. `(add ,x ,y) is a pattern that checks that form is a three-element list starting with the literal symbol add, then extracts the second and third elements and binds them to symbols x and y, respectively.

The commas are used to “extract” elements of the section-fn and allow them to be used later on. I’m trying to connect this to my extensive Ruby (Ruby 📖) experience, and I’m struggling to do so.

I’ve used case statements before, but I hadn’t considered how I might use comparison statement as the thing that also “declares” the variables for evaluation.

Thank You

So thank you Jethro for taking the time to refine the org-roam code. This has helped me further develop my understanding of Emacs.