日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

On Lisp -> Clojure へ移植: 6.9~6.11

6.9 マクロへの依存

p66

Clojure も事情は同じと考えていいのかな。

(defmacro mac [x] `(inc ~x))
;=> #'user/mac
(def f (fn [y] (mac y)))
;=> #'user/f
(defmacro mac [x] `(+ ~x 100))
;=> #'user/mac
(f 1)
;=> 2

6.10 関数からマクロへ

p67

ここでは Clojure 定義に合わせてみる。

(defn second [x]
  (first (next x)))

(defmacro second [x]
  `(first (next ~x)))

(defn noisy-second [x]
  (println "Someone is taking a second!")
  (first (next x)))

(defmacro noisy-second [x]
  `(do
     (println "Someone is taking a second!")
     (first (next ~x))))

sum マクロ。この場合 reduce でも同じ。

(defn sum [& args]
  (apply + args))

(defmacro sum [& args]
  `(+ ~@args))

p68

foo のまずは関数版。

(defn foo [x y z]
  (list x (let [x y]
            (list x z))))

foo のマクロ版は Clojure の場合この定義で使おうとしても、

(defmacro foo [x y z]
  `(list ~x (let [x ~y]
             (list x ~z))))
;=> #'user/foo

let で修飾名は使えないよと怒られる。

(foo 1 2 3)
;=> CompilerException java.lang.RuntimeException: Can't let qualified name: user/x, compiling:~

なので foo 関数の Clojure マクロ版を作るなら仮引数を参照しない let 以降の x は auto gensym させてやるべきか。On Lisp ではまだ紹介されてない技法だけども。

(defmacro foo [x y z]
  `(list ~x (let [x# ~y]
              (list x# ~z))))

その前にせめて自分で定義した関数内で仮引数とマクロ内変数の名前衝突はさせないようにした方が無難だとは思うけど。

6.11 シンボル・マクロ

symbol-macrolet は Clojure でも clojure/tools.macro 外部ライブラリを導入すれば使えるようになるみたいだけど、実際の導入は使われていると言う 15 章と 18 章に持ち越す。

第 6 章 マクロ

はここまで。