Clojureの「..」「doto」「->」「->>」マクロの使い方覚え書き

More than 5 years have passed since last update.


..マクロ

    ;; before -- getの引数とレシーバがわかりにくい.

(.get (.getChildren pane) 2)
;; before2 -- こう書くことも出来るけど`(.`が連続して読みにくい
(. (. pane getChildren) (get 2))

;; after -- ..マクロを使うとこう書ける. macroexpandするとbefore2に等しくなる
(.. pane getChildren (get 2))

    // Java

pane.getChildren().get(2)


  • 前置記法のわかりにくさ回避. 左から右, メソッドチェイン風に書ける.

  • 引数をとる関数適用は(func arg)という形で置く.

  • 引数なしの関数適用はfuncを単体で書けば良い.

  • Javaメソッド(or フィールドの参照)にのみ使える


    • この点を除けば -> マクロとほぼ同じ.

    • Javaメソッドであることを明示でき可読性up.

    • ..マクロは必ずJavaメソッドを呼ぶので, 単独での利用時に頭につけていたドットが不要に.




dotoマクロ

使用例: tableに対する破壊的操作が連続してる.

    (.. table (getColumns (addAll [titleColumn artistColumn releaseColumn])))

(.. table (setItems albums))
(.. table (setEditable true))

これをdotoでまとめると

    (doto table

(-> .getColumns (.addAll [titleColumn artistColumn releaseColumn]))
(.setItems albums)
(.setEditable true))

となり, コードを読む際に副作用ある式のマーカーとして可読性を上げてくれる.

なお「..マクロ」を使わなくなったのでJavaメソッドを呼ぶ頭のドットが復活している.

ちなみに上の例をmacroexpandするとこうなる.

(let* [G__1278 table] ;; 破壊的操作を加えるのでtableをコピー

(-> G__1278 .getColumns (.addAll [titleColumn artistColumn releaseColumn]))
(.setItems G__1278 albums)
(.setEditable G__1278 true)
G__1278) ;; 最後に破壊的操作を加えたtableをreturn

最後に破壊的操作を加えた結果を返してくれるので, let中で初期化と同時にいろいろパラメータをセットしてやるときに便利.

(defn drawCircle [i pane]

(let [circle (doto (Circle. (* (+ 50 i) 10))
(.setFill (Color/color (- 1.0 (/ i 10.0))
0
(/ i 10.0))))]
(.. pane getChildren (add circle))))

こんな感じに.


->マクロ


  • 左から右にメソッドチェイン. 「..マクロ」とほぼ同じ

    ;; 使用例

(-> "../resources/layout.fxml" io/resource FXMLLoader/load)

;; 動作理解のための意味のない例
(-> 3 (+ 3)) ;; => 6
(macroexpand '(-> 3 (+ 3))) ;; => (+ 3 3)
(macroexpand '(-> 3 (+ 1 2))) ;; => (+ 3 1 2)


->>マクロ

->マクロ とは作用順序が異なる.

    (macroexpand '(->  3 (+ 1 2))) ;; => (+ 3 1 2)

(macroexpand '(->> 3 (+ 1 2))) ;; => (+ 1 2 3)

作用順序によって結果が異なるケースの例

    (macroexpand '(->  10 (* 10) (- 1))) ;; => (- (clojure.core/-> 10 (* 10)) 1)  ;; 100 - 1

(macroexpand '(->> 10 (* 10) (- 1))) ;; => (- 1 (clojure.core/->> 10 (* 10))) ;; 1 - 100
(-> 10 (* 10) (- 1)) ;; => 99
(->> 10 (* 10) (- 1)) ;; => -99

->>はここで必要なのか!という例にまだ出会ってないので, 見つけたら追記する.