アナフォリックマクロ

Programming ClojureのChapter 7 Macroの途中まで読んだので、マクロを使ってみた。

アナフォリックマクロを作成しようとして、

sample/macro.clj

(ns sample.macro)

(defmacro aif [pred then]
  `(let [it ~pred]
    (if it ~then)))

を定義し、実行すると、

user> (use 'sample.macro)
nil
user> (aif (+ 1 2 3) (println it))
; Evaluation aborted.
Can't let qualified name: sample.macro/it
  [Thrown class java.lang.Exception]

エラーとなる。

Clojureでは、マクロ展開時のシンボル補足によるバグを防ぐため、「`」でクオートされた構文中にシンボルが表われた場合は、そのシンボルはqualified nameになる。
上記の aifの場合は、it は sample.macro/it となるため、エラーとなる。

user> (macroexpand '(aif (+ 1 2 3) (println it)))
(let* [sample.macro/it (+ 1 2 3)] (if sample.macro/it (println it)))

アナフォリックマクロのように、シンボル補足を利用するマクロの場合は、補足したいシンボルに~'を付ければ良い。

(defmacro aif
  ([pred then] `(aif ~pred ~then nil))
  ([pred then else] 
     `(let [~'it ~pred]
	(if ~'it ~then ~else))))
user> (aif (+ 1 2 3) (println it))
6
nil
user> (aif false (println it) (println it))
false
nil

動いた。\(^▽^)/