Code Aquarium

minazoko's blog -*- 水底のブログ -*-

Clojure1.5 の New Thread Macro

Githubを眺めていたらClojure1.5でcoreに追加される新しいスレッドマクロが目にとまりました。スレッドマクロ大好きなのでちょっと気になり試してみました。

● as->

これが一番分かりやすく出番もありそうです。

(require '[clojure.string :as string])

(as-> "0x01-0x02-0x03-0x04-0x05-0x09-0x10-0x20" it
      (string/replace it #"\b0x(\d+)\b" "$1")
      (string/split it #"-")
      (map #(Long/parseLong % 16) it)
      (conj it -1)
      (apply * it))

;;=> -552960

適当なコードですが、ポイントは第二引数のシンボル(ここでは it)です。スレッド内の各フォームの処理結果が第二引数のシンボル名に束縛されるので、次のフォームへ渡す時フォーム内の好きな位置に適用できます。
->や->>だと適用位置が固定され使いづらいことがありますがその不満に対するソリューションですね。*1

● cond->, cond->>

(defn divisible? [n m]
  (zero? (rem n m)))

(defn fizzbuzz [n]
  (or (cond-> nil
              (divisible? n 3) (str "Fizz")
              (divisible? n 5) (str "Buzz")
              (divisible? n 7) (str "Foo")
              (divisible? n 11) (str "Bar"))
      n))

(map fizzbuzz (rest (range 100)))

;;=> (1 2 "Fizz" 4 "Buzz" "Fizz" "Foo" 8 "Fizz" "Buzz" "Bar" "Fizz" 13 "Foo" "FizzBuzz" 16 17 "Fizz" 19 "Buzz" "FizzFoo" ...

第一引数が初期値。以下condのように「条件式」と「処理内容」が交互に並び、条件が成立した時だけ隣のフォームが評価されます。判定は上から順に行われ、前の処理の結果が次の処理内容の第一引数に渡されます。条件が不成立なら何もせずにその次のフォームへ流れます。
「処理するフォームを選択できる -> 」または「処理したくないフォームをスキップできる -> 」と言ったら分かりやすいでしょうか?
cond->>もありますが、これは->に対する->>の関係と同じです。

● some->, some->>

これは要するにclojure.core.incubator-?>, -?>>です。途中で評価値がnilになった場合にそれ以降の処理は行われず全体の評価がnilになります。

(defn catch->nil [x f]
  (try (f x) (catch Exception _ nil)))

(defn calc [x]
  (some-> x
          (catch->nil #(- % 10))
          (catch->nil #(/ 1 %))
          (catch->nil #(+ 100 %))))

(calc 1)      ;=> 899/9
(calc 5)      ;=> 499/5
(calc 10)     ;=> nil   (zero divide error)
(calc "hoge") ;=> nil

一種のエラー処理に使えます。

● まとめ

as-> は使えそうですね。cond->, cond->> は微妙ですが場合によっては使えるかも?*2
some-> はもともと -?> をあまり使っていないので出番はなさそうですね。

*1:よく似たマクロを考案していたのですが、公式で用意されてしまったのでお蔵入りです。

*2:cond-as-> が欲しくなりそうな気がします。