Help us understand the problem. What is going on with this article?

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

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

hash
機能が向上し, 問題が修正されています
http://twitter.com/T_Hash
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away