TL;DR

  • Clojure のスレッディングは、並行処理のスレッドとは関係なく、コードを直感的に書く方法である。
  • -> は値を次のフォームの第 1 引数に、->> は最終引数に、as-> は任意の位置に渡す。
  • cond-> と cond->> は条件ごとにスレッディングする。

内容

他の言語でも使われるスレッド(threads)と、Clojure で使われるスレッディング(threading)は全く関係ないので注意する。スレッディングはコードの塊を並行して実行することを指すのではなく、Clojure でコードを直感的に書く方法を指す。

-> マクロ

最初に指定された値(5)を、次のフォーム((Math/pow …))の第 1 引数として渡す。これを最後のフォームまで繰り返す。

(-> 5
		(Math/pow 2)) ; 25

そのため、上の式は次と同じ意味になる。

(Math/pow 5 2) ; 25

-> マクロは一連のフォームの処理をまとめるときに真価を発揮する。

(-> 10
    inc
    (* 2)
    (+ 5))

上の式は次と同じ意味になる。

(+ (* (inc 10) 2) 5)

比べると、-> マクロを使ったほうが読みやすい。

->> マクロ

-> と似ているが、値が入る位置が最終引数の位置になる点が違う。

(->> 5
     (Math/pow 2)) ; 32

そのため、上の式は次と同じ意味になる。

(Math/pow 2 5) ; 32

こちらも、一連のフォームの処理をまとめるときに真価を発揮する。

(->> 5
     (Math/pow 2)
     inc) ; 33

上の式は次と同じ意味になる。

(inc (Math/pow 2 5))

as-> マクロ

-> や ->> と違い、関数の任意の位置に値を渡せる。

(as-> 5 x
  (Math/pow x 2)) ; 25

このコードでは変数 x に 5 を束縛し、Math/pow の第 1 引数として使っている。同じように、Math/pow の第 2 引数にするときは次のように書ける。

(as-> 5 x
  (Math/pow 2 x)) ; 32

cond->, cond->> マクロ

条件ごとにスレッディングしたいときは、cond-> と cond->> マクロを使える。cond-> と cond->> の違いは、-> と ->> の違いと同じく、値が次のフォームの第 1 引数に渡されるか最終引数に渡されるかだけである。

まず、cond-> の例を見る。

(let [a 11]
  (cond-> []
    (odd? a) (conj (* a 2))
    (even? a) (conj (/ a 2)))) ; 22

このコードでは、初期値として空配列([])を渡し、a が奇数か偶数かで処理を分けている。

  • 奇数のとき、a を 2 倍した値を空配列に追加する(実際に処理されるコード: (conj [] (* a 2)))
  • 偶数のとき、a を 1/2 にした値を空配列に追加する(実際に処理されるコード: (conj [] (/ a 2)))

今回は a が 11 なので、(conj [] (* 11 2)) が実行され、[22] を得る。

同じように、cond->> の例を見る。

(let [a 10]
  (cond->> 1
    (odd? a) (* a 2)
    (even? a) (/ a 2))) ; 5

このコードでは、初期値として 1 を渡し、a が奇数か偶数かで処理を分けている。

  • 奇数のとき、(* a 2 1) を実行する
  • 偶数のとき、(/ a 2 1) を実行する

今回は a が 10 なので、(/ 10 2 1) が実行され、5 を得る。

キーワード

自分の理解

次の資料を参考にした。