One of the early mental hurdles that must be cleared when learning Clojure is the application of threading macros.
Usually the first macro we learn is either the thread-first (->
) or thread-last (->>
) macro. Sometime later, if you read the docs, you'll probably encounter the remaining threading macros:
The difficult thing about the thread-first and thread-last macros is that you have to mentally fill in the results being 'threaded' through the forms. Here's an interesting example from the thread-last docs:
;; An example of using the "thread-last" macro to get
;; the sum of the first 10 even squares.
(->> (range)
(map #(* % %))
(filter even?)
(take 10)
(reduce +))
The difficult thing for the beginner is to imagine the result of each form being 'threaded':
(->> (range)
(map #(* % %) <result-of-range>)
(filter even? <result-of-map>)
(take 10 <result-of-filter>)
(reduce + <result-of-take>))
If you simplify <result-of-*>
(above) to something like $
you get this:
(->> (range)
(map #(* % %) $)
(filter even? $)
(take 10 $)
(reduce + $))
...which is remarkably similar to the exact mechanics of as->
:
(as-> (range) $
(map #(* % %) $)
(filter even? $)
(take 10 $)
(reduce + $))
Here's an example of how I used as->
to prepare some hand-crafted html for a test assertion against the output from hiccup:
(let [tags (my-function-to-generate-tags)
lines
["<html> "
" <head> "
" </head> "
" <body> "
" <h1> "
" Welcome! "
" </h1> "
" <form action='/other/page' method='POST'> "
" <label for='foo'>Enter Foo:</label> "
" <input id='foo' name='foo' type='number' value='3' /> "
" <input type='submit' value='Foo' /> "
" </form> "
" </body> "
"</html> "]]
(should= [:html ...] tags)
(as-> lines $
(map string/trim $)
(apply str $)
(string/replace $ "'" "\"")
(should= $ (hiccup/html tags))))
Conclusion
The as->
macro is much more flexible than ->
or ->>
and allows for easier visualization of what the macro is doing for you. Let's use it instead to introduce the concept of threading macros!
Part 2
But wait, there's more! Head over to part 2.