Threading Macros in Clojure - Thread-Last vs Thread-First vs As
Nested calls can become hard to manage. Threading macros help us take control. In this chapter, we'll learn about the concept of threading macros and study three commonly-used versions.
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the Tinycanva: Clojure for React Developers course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Tinycanva: Clojure for React Developers, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:10] Threading macros. The term thread has got nothing to do with parallel programming here. Thread is an expression for a linear series of operations like pearls on a thread.
[00:11 - 00:27] As your functions grow in complexity, the readability of code gets affected. Suppose you have a list of book with various attributes like restricted or related books and you want to find the number of books related to each unrestricted book.
[00:28 - 00:34] Your code might look something similar to this. Calls like these might go out of hand.
[00:35 - 00:40] What happens if we want the publishing year of all related books? We perform the following operations.
[00:41 - 00:53] First we filtered all books that are restricted and then called publish here by id function on each element of the related vector. The thread last macro makes nested function calls easier to express.
[00:54 - 01:08] A nested function called f of g of yx becomes this with the thread last macro. In thread last macro expansion, the result of a form is passed as the last argument to the next form.
[01:09 - 01:19] Let's rewrite our publish here by id function with thread last macro. Notice how we are able to use first function and publish here keyword without parentheses.
[01:20 - 01:32] You can add a parentheses if you wish to but closure resumes parentheses in case a callable form is passed. Most developers find forms using threading macros more readable.
[01:33 - 01:40] Let's rewrite the other operation using thread last macro too. It is still not readable as the pub year by id function.
[01:41 - 01:50] You would constantly run across operations like these in real world applications. If I were writing this, I'd pull out the inline function to a namespace function.
[01:51 - 02:04] We split the operation down into normal functions which are easier to maintain and reason for. There is no performance cost associated with using a macro because macros rewrite your code at build time.
[02:05 - 02:18] You can check what your code would be rewritten to using the macro expand operation. The part with string and js star is just a conversion so that the result is suitable for JavaScript.
[02:19 - 02:24] But you can see how plus is called with 1 and 2. Notice the single code before the start of the form.
[02:25 - 02:40] The code mark tells closure to treat this form as a list and not as a function call. The thread first macro is similar to thread last macro except the result of the previous form is passed to the next form as the first argument.
[02:41 - 02:47] Hence the name thread first. Here we compute the total number of books related to the first book in our collection.
[02:48 - 03:01] Thread last macro is suitable for sequence operations as they generally accept the sequence as the last argument. The thread first macro is commonly used to pull out values from deeply nested maps.
[03:02 - 03:14] You might run into cases where some functions in a pipeline accept arguments at different positions than first or last. The as macro can be used to define the position of the argument.
[03:15 - 03:27] It works by binding the result of a step to a variable and then manually passing that variable at the correct location. Here we bound the output to variable x and passed it at the correct position.
[03:28 - 03:37] The bindings can be destructure too. Here we started with a map and pulled out the keys host and port inside the as macro.
[03:38 - 03:48] In this chapter we learned the usage of threading macros to improve code read ability. We covered the three most commonly used macros and also learned about macro expansion.
[03:49 - 04:03] The three macros should suffice for this course but if you find yourself wanting more we also have the quant arrow, quant double arrow, sum arrow and some double arrow macros. You can read more about them in the official docs.