Did you know about -> and ->>?

This is the second post in my „Did you know?“ series. This time we will have a closer look at -> and ->>.

From `->` to `->>` and back again

We all know the two threading forms which help a lot with chains of calls. -> threads on the first position, ->> on the last. Here some examples.

; This -> form
(-> a b (c d) (e f) g)
; expands to
(g (e (c (b a) d) f))
; And this ->> form
(->> a b (c d) (e f) g)
; expands to
(g (e f (c d (b a))))

Sometimes you have one or two one calls in the middle of the chain which require the other form. Then one has to split the chain.

(let [first-part  (-> a b (c d))
      second-part (->> first-part (map h) (filter pred))]
  (-> second-part (e f) g))

However, we can actually simplify this!

(-> a
  b (c d)
  (->> (map f) (filter pred))
  (e f) g)

Going the other way around is not so trivial. Trying the naive approach we get the following.

(->> x (map f) (-> (get :x)) (filter pred))
; expands to
(filter pred (map (get :x) f x))

Not quite what we want. The reason is, that ->> inserts the expression at the end of the next form. This is also the case for the contained -> form. And from there on things go southwards.

However with a small trick, we can still get what we want: we simply have to start with ->.

(-> a
  (->> (map h) (filter pred))
  (c d) (e f)
  (->> (remove second-pred) (reduce i)))

Here is a practical example (allthough a little stretched): a column index to Excel column converter.

user=> (def column-chars "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
#'user/column-chars
user=> (defn index-to-column
         [idx]
         (->> idx
           (iterate #(-> % dec (quot 26)))
           (take-while (complement zero?))
           (map #(-> % dec (rem 26) (->> (nth column-chars))))
           reverse
           (apply str)))
#'user/index-to-column
user=> (index-to-column 1)
"A"
user=> (index-to-column 26)
"Z"
user=> (index-to-column 27)
"AA"
user=> (index-to-column 677)
"ZA"

Did you also know about `doto`?

And finally – as a small teaser that there is more to explore – there is also doto. Consider this Swing example.

(doto (JFrame.)
  (.setDefaultCloseOperation JFrame/DISPOSE_ON_CLOSE)
  (-> .getContentPane (.add (JButton. "Hello")))
  (.setVisible true))

There is a lot to explore in Clojure. Go on and experiment!

Published by Meikel Brandmeyer on .