..マクロ
;; 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
->>
はここで必要なのか!という例にまだ出会ってないので, 見つけたら追記する.