DEV Community

Cover image for Clojure 101 / threading macros
icncsx
icncsx

Posted on

Clojure 101 / threading macros

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))
Enter fullscreen mode Exit fullscreen mode

(->> 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 +))
Enter fullscreen mode Exit fullscreen mode

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

Top comments (3)

Collapse
 
mxldevs profile image
MxL Devs

Interesting syntax.

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

Collapse
 
icncsx profile image
icncsx • Edited

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!

Collapse
 
mxldevs profile image
MxL Devs

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.