loading...
Cover image for Clojure 101 / threading macros

Clojure 101 / threading macros

icncsx profile image DH Kim ・1 min read

When you have deeply nested functions or transformations, it might help you to consider a thread macro which helps the readability of your code.

(-> x & forms)

-> is the thread-first macro. It evaluates one form and passes it as the first argument into the next form.

;; these two are equivalent
(.toUpperCase (first ["chicken" "egg"]))
(-> ["chicken" "egg"] first .toUpperCase)

;; and so are these
(assoc {} :key 24)
(-> {} (assoc :key 24))

(->> x & forms)

->> is the thread-last macro. It evaluates one form and passes it as the last argument into the next form.

;; these two are equivalent
(apply + (map (fn [x]
                (* x 2)) [1 2 3]))

(->> [1 2 3]
     (map (fn [x] (* x 2)))
     (apply +))

;; and so are these
(reduce +
     (filter even? [1 2 3 4]))

(->> [1 2 3 4]
     (filter even?)
     (reduce +))

Here is a dad joke in case you need a laugh:

The spool of the thread asked the needle, "How you doing?" The needle replied, "sew sew."

I'm out.

Warmly,
DH

Posted on by:

icncsx profile

DH Kim

@icncsx

Software engineer interested in cloud computing and serverless backend services.

Discussion

markdown guide
 

Interesting syntax.

Though I'm curious why the thread-macro version makes it more readable than the non-thread-macro.

 

Hello! Thanks for visiting.

To answer your question, it's totally a matter of preference, but some devs like the most inner arg - typically data x - to be the first thing they see. So instead of f(g(h(i(x))), if you have (x ->> i h g f), you know right off the bat x is the thing we're working with and it's to go through a series of transformations in the order i, h, g, and f. The benefit of a threading macro becomes more obvious if you have really really nasty nesting going on.

Happy coding!

 

Thanks for the explanation. I can see why it could be easier to know the argument first when tracing through a complex sequence of calls.

Now that I think about it, even in natural language, you can have an instruction like "uppercase the first letter of each word" which makes grammatical sense in english, but then once the instructions become more complex like "uppercase the first letter of every other word whose length > 3", it's easier for me to translate the logic in reverse the same way the thread-macro is written so the the argument in question (each word) comes first.