🐈️

スレッディングマクロの展開の違い

2024/11/17に公開

マクロを展開させて遊んでいるときに、「スレッディングマクロと名前がついているくらいだしこれもマクロだよな」となったので、展開させて遊んだ記事です。
スレッディングマクロの違いについて気になったので、macroexpand-1を用いて簡単に調べた記事です。

なお、今回取り上げるのは->->>になります。

スレッディングマクロって何?

以下の記事に詳しいですが、「カッコでネストされる読みづらく書きづらい記法を簡潔に書くためのマクロ」と考えていいと思います。

https://note.com/mi_nil/n/n78f1701bccb0

スレッディングマクロを使わない場合は、読みづらく書きづらいが、

;; ある数xがあった場合。
(h (g (f x)))

;; 例として以下のようなコードが考えられる。

;; 単純なincの適用。
(inc (inc (inc 1)))
;; 4

;; 50円の商品を10個買った合計値(税率10%)。
(str "total: "
     (int
      (* 1.10
         (* 10 50))))
;; "total: 550" 

スレッディングマクロを使うとすっきりする。

(-> x
    f
    g
    h)

;; 実例を同じようにすると、以下のようになる。

;; 単純なincの適用。
(-> 1
    inc
    inc
    inc)

;; 50円の商品を10個買った合計値(税率10%)。
(->> 50
     (* 10)
     (* 1.10)
     int
     (str "total: "))
;; "total: 550"

マクロ展開の検証

さて、これをmacroexpand-1を使って展開してみましょう。

(macroexpand-1 '(-> x f g h))
;; (h (g (f x)))

(macroexpand-1 '(->> x f g h))
;; (h (g (f x)))

一見、何も違いが無いように見えますが...

(macroexpand-1 '(-> x (f 1) (g 2 3) h))
;; (h (g (f x 1) 2 3))

(macroexpand-1 '(->> x (f 1) (g 2 3) h))
;; (h (g 2 3 (f 1 x)))

展開する引数の順番が異なるようです。

この展開の違いを意識していないと、意図しない結果となることに注意が必要です。

;; ->> ではなく -> にしたところ、意図しない結果となってしまった。
(-> 50
    (* 10)
    (* 1.10)
    int
    (str "total: "))
;; "550total: "

終わりに

今回はスレッディングマクロのうち->->>を挙げましたが、他にもスレッディングマクロはあるようなので(公式のガイドを参照)、分かり次第遊んで検証してみたいと思います。

参考

https://clojuredocs.org/clojure.core/macroexpand-1

https://clojure.org/guides/threading_macros

https://note.com/mi_nil/n/n78f1701bccb0

GitHubで編集を提案

Discussion