Clojureの->
->>
スレッディングマクロで前回フォーム結果を任意の場所に展開したい場合、以下のようにラムダを使用することができます。
(->> (range 10) (map -) (#(nth % 3)))
ただ、この方法だと (#(
となるのが少し気持ち悪いです。また、ラムダはネストすることができないので、なるべく節約したいところです。
as->
マクロもありますがas->
は毎回場所を指定しないといけないので面倒です。
そこで、シンボルとして<>
が出現したときだけその場所に前回フォーム結果が展開され、<>
がないときは->
->>
と同じ動作をするスレッディングマクロとしてo>
o>>
を作ってみます。
optional_threading_macro.clj
(defn cons-nth [index x coll]
(concat (take index coll) [x] (drop index coll)))
(defn cons-last [x coll]
(concat coll [x]))
(defn- find-symbol [form sym]
(cond (or (sequential? form) (set? form)) (some #(find-symbol % sym) form)
(map? form) (or (find-symbol (keys form) sym)
(find-symbol (vals form) sym))
:else (= form sym)))
(defmacro o> [x & forms]
(loop [x x forms forms]
(if-not forms x
(let [form (first forms)
threaded (if (seq? form)
(-> (if (find-symbol form '<>)
(list 'let ['<> x] form)
(cons-nth 1 x form))
(with-meta (meta form)))
(list form x))]
(recur threaded (next forms))))))
(defmacro o>> [x & forms]
(loop [x x forms forms]
(if-not forms x
(let [form (first forms)
threaded (if (seq? form)
(-> (if (find-symbol form '<>)
(list 'let ['<> x] form)
(cons-last x form))
(with-meta (meta form)))
(list form x))]
(recur threaded (next forms))))))
このマクロを用いたサンプルは以下のとおりです。<>
が現れたときのみ、その場所に前回フォーム結果が展開されています。
user=> (o>> 10 (range) reverse (map -) (nth <> 3) (identity {:a [1 <>]}))
{:a [1 -6]}
user=> (o> 10 (range) reverse (nth 3) (map - (range <>)))
(0 -1 -2 -3 -4 -5)