LoginSignup
41
32

More than 5 years have passed since last update.

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

Last updated at Posted at 2013-09-28

..マクロ

    ;; 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

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

41
32
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
41
32