Clojure's threading macros

by Taylor Fausak on

I recently learned Clojure. It was my first experience with a Lisp, and I enjoyed it. It felt like a mix of Ruby, which I use in my day job, and Haskell, which I use in my side projects.

One thing in particular stood out to me: threading macros. Some of the ones in the standard library are ->, ->>, and as->. They elegantly solve writing imperative-style code in a functional language.

They stood out to me because they’re like a much better version of Flow, one of my Haskell libraries. Since functions with a variable number of arguments are hard in Haskell, Flow only works with functions of a single argument. Clojure’s threading macros can work with any number of arguments in any position.

If you aren’t familiar with them, here is a quick overview of the threading macros. -> is “thread first”. It is easiest to explain with examples.

(-> x
    f)
;; (f x)
(-> x
    f
    g)  
;; (g (f x))
(-> x
    (f y))
;; (f x y)

It fills in the first argument and carries it through all the forms. If you want to fill in the last argument, use ->>, the “thread last” macro.

(->> x
     f)
;; (f x)
(->> x
     f
     g)  
;; (g (f x))
(->> x
     (f y))
;; (f y x)

If you need more control than that, you can use as->, the “thread as” macro. It allows you to fill in an arbitrary argument.

(as-> x $
      (f $))
;; (f x)
(as-> x $
      (f $)
      (g $))
;; (g (f x))
(as-> x $
      (f $ y))
;; (f x y)
(as-> x $
      (f y $))
;; (f y x)
(as -> x $
      (f $ y)
      (g z $))
;; (g z (f x y))

These macros are a powerful way to write expressive functional pipelines. I was surprised to find that they were idiomatic Clojure, considering the backlash Flow got from the Haskell community. That makes me think that Clojurists are more pragmatic than Haskellers.

Regardless, this is a neat feature of Clojure. I wish Haskell had something similar. Unfortunately I think it would require Template Haskell, but it could be fun to write functions like this:

$(as-> x _
       (f _ y)
       (g z _))
-- (g z (f x y))