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.'")))))
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
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
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
(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
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
pcasepattern 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
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.
So thank you Jethro for taking the time to refine the
org-roam code. This has helped me further develop my understanding of Emacs.