1. what are threading macros? where do they come from?

For example, what is this

(-> 2 (expt 0.2)(* 2017) sin); ⇒ -1.0

The Clojure language has some neat macros (such as -> and ->>) which are called threading macros (nothing to do with these threads) which let you unwrap some types of deeply nested forms. The dash library for Emacs Lisp implements some of those threading macros. While I do not use threading macros in my elisp code, I know that elisp beginners would love using these macros, so I wrote this article.

Prerequisites: Readers are not required to be familiar with Clojure or dash, but you are assumed to be able to install the dash library. Unwillingness to check the length of the word humuhumunukunukuapuaa by hand is required.

Common Lisp Note: These threading macros can be defined in Common Lisp too. When you define them, don’t forget that threading macros are more than just function call chaining. What I mean by that? I’ll get to that soon.

2. how to use the dash library

After you install the dash library, you will have to put (require 'dash) somewhere in your init file. Where?

You probably have something like the following two lines in your init file.

(package-initialize)(setq package-enable-at-startup nil)

You want to make sure that the line (require 'dash) runs after (package-initialize) runs, but before any code that relies on functions or macros from the dash library runs. Simplest way to do that is to put (require 'dash) right after the package-initialize lines so that your init file code looks like:

3. thread-first nesting and the thread-first macro

Before we begin, let’s talk about deeply nested forms.

Let’s start with:

(f3 (f2 (f1 x c1 d1) c2 d2) c3 d3)

That is an f1 form within an f2 form within an f3 form. If f1, f2, f3 were functions (as opposed to macros), that could mean “take object x, apply f1 to it with additional arguments c1 and d1, then apply f2 to the result with additional arguments c2 and c2, then apply f3 to the result …”.

To help make your code more readable, you could write that form in multiple lines as:

(f3 (f2 (f1 x
c1 d1)
c2 d2)
c3 d3)

or:

(f3 (f2 (f1 x
c1
d1)
c2
d2)
c3
d3)

or in combination of both styles depending on which arguments are complex forms themselves.

Alternatively, you can use the macro -> (the “thread-first” macro from the dash library) to write this instead:

(-> x
(f1 c1 d1)(f2 c2 d2)(f3 c3 d3))

4. example uses of the thread-first macro

There is a saying,”never date anyone under half your age plus seven”. Suppose you are a 200 year old turtle. You are not supposed to date turtles under age 107. You take the number 200, divide it by 2, then add 7, that’s 107. You can compute that with this form which you must read inside-out:

(+ (/ 200 2) 7)

You can also write the same computation using the -> macro like this which you can read from left to right rather than inside-out:

(-> 200 (/ 2)(+ 7))

Some argue that writing an inside-out expression is unnatural for humans, but I heard from somewhere that the English expression “Sum the balance of all savings accounts” is a perfectly natural inside-out expression (inside-out from a procedural perspective). The threading macros (and serial binding forms that I will get to) give you choice: you can either write an inside-out expression or an expression to be read from left to right (or from top to bottom).

You’ve seen an example of a -> form that you read left to right. Now let’s see an example that you read from top to bottom. The following code starts with a long list, then removes duplicates from the list, then removes 0s and 1s, and then sorts it.

5. side note on fear of deeply nested forms

Some Lisp beginners tend to fear reading and writing of deeply nested forms (even three or four levels of nesting could feel too deep). Since this article tend to attract those beginners, I’d like to include my explanation for why you should not fear.

For reading deeply nested forms, sometimes keybindings for structural movement (for example, C-M-u) help a lot when reading from indentation seems not enough. For writing, with paredit you will be able to figure out a way to write a nested form from inside out, or from outside in, or whatever order you choose to write. With these tips in mind, one can eventually overcome fear of something like:

10. threading macros are more than serial binding

Threading macros can be more than just chaining function calls because you can use them with other macros like loop macros or conditionals. For example, you can write your own REPL (Read Eval Print Loop) like this:

11. closing notes

Everything I want beginners to know for this topic is covered now. The rest is optional reading.

12. optional reading

12.1. sum under reciprocal

Alice takes 30 minutes to finish a bowl of jjajangmyeon. Bob takes 40 minutes to finish the same. With Alice and Bob working together on the same one bowl of jjajangmyeon, how many minutes does it take to finish the bowl? Sum of 30 minutes and 40 minutes under reciprocal. To calculate it,

12.2. art of minimizing use of thread-middle macro

In Clojure, consensus seems to be that Clojure libraries should be designed in such a way that users usually only have to use just one of the thread-first macro and the thread-last macro just once for a group of steps. The dash library and the s library are two Emacs Lisp libraries that sticks to that Clojure consensus and that is a sort of selling point of the two libraries. For example, many functions from dash that work on lists consistently take the list as the last argument so that you can use just the thread-last macro with them. If you want to get the most out of threading macros, you may want to start depending on functions from the two libraries.

My examples in this article show some reliance on CL-LIB functions (rather than functions from the two libraries: dash and s) because I tend to depend on CL-LIB functions and also because I am not assuming the readers to be familiar with functions from the two libraries. (I tend to use CL-LIB more because it’s shipped with Emacs.)

Clojure programmers sometimes come to a situation where they have to write a form that seems to require two or three times last-argument threading and just one first-argument or middle-argument threading. In that case, some of them tend to use a neat trick to manage to write it with the thread-last macro (rather than write it with the thread-middle macro). An Emacs Lisp equivalent would be, for example, you might be using the s library and you want to take a list of strings, trim them, then join them with comma, and then wrap the result in curly braces using just the threading-last macro, but you are wondering what to do with the last step. You can just do this: