マクロ定義自体が、eval
を呼び出すなどしてマクロ引数式のいずれかを評価する場合、別の問題が発生する可能性があります(Eval
を参照)。マクロ展開は、コードが実行されるずっと前、呼び出し元のコンテキスト(マクロ展開が評価される場所)がまだ参照できない段階で発生する場合があることに留意する必要があります。
また、マクロの定義で lexical-binding
を使用していない場合、その仮引数が同名のユーザー変数を隠蔽することがあります。マクロ本体内では、マクロ引数の束縛が当該変数の最も局所的な束縛となるため、評価対象の式内のあらゆる参照はその変数を指すことになります。以下に例を示します。
(defmacro foo (a) (list 'setq (eval a) t))
(setq x 'b)
(foo x) → (setq b t)
⇒ t ; and b has been set.
;; but
(setq a 'c)
(foo a) → (setq a t)
⇒ t ; but this set a, not c.
ユーザーの変数名が a か x
かによって結果が変わります。というのも、a はマクロの引数変数 a と衝突するためです。
また、上記の (foo x)
の展開は、コードのコンパイル時に異なる結果を返すか、あるいはエラーを発生させます。というのも、この場合 (foo
x) はコンパイル時に展開されるのに対し、(setq x 'b)
の実行はコードの実行時になってから行われるためです。
これらの問題を回避するには、マクロ展開の計算中に引数式を評価しないでください。代わりに、その式をマクロ展開に代入することで、展開の実行過程でその値が計算されるようになります。本章の他の例も、この仕組みで動作しています。