健全なマクロ()とは、マクロ処理の過程で見かけ上同じに見える識別子が発生(名前が衝突)しても、意図しない問題が起こらないことが保証されているマクロである。Scheme、Dylan、Rust、Nim、Juliaなどのプログラミング言語がこの機能を持っている。

マクロ処理の過程で見かけ上同じに見える識別子が発生するという問題は、健全なマクロが導入される以前からLISPコミュニティで広く知られていた。マクロの作者は、プログラムコード中の他のどれとも異なるユニークな識別子を生成する gensym などの組み込み関数を使ってプログラマーが明示的に回避したり、通常使用されないような長くて複雑な名前の識別子を用いることでこの問題の可能性を低減させてきた。

健全なマクロはこの問題を解決する方法をマクロの展開機能に組み込むことで、問題を根本的に解決している。

「健全」を意味する「」という語は、コールベッカーらが1986年に書いた健全なマクロの展開を導入した論文で、数学の用語の影響を受けて初めて使用された。

健全でないマクロ 健全なマクロの機能を持たないプログラミング言語では、マクロの展開中に作成された変数の束縛によって、すでに存在する変数の束縛が隠されてしまう可能性がある。C言語では、次のようなコードによってこの問題を説明できる。#define incr(X) { int a=0; ++X; } int main(void) { int a = 0, b = 0; incr(a); incr(b); printf("a=%d, b=%d\n", a, b); return 0; }C言語のプリプロセッサで上のコードを変換すると、次のコードが生成される。int main(void) { int a = 0, b = 0; { int a=0; ++a; }; { int a=0; ++b; }; printf("a=%d, b=%d\n", a, b); return 0; }main のスコープで宣言された変数 a は、マクロの中にある変数 a によって隠されてしまっている。その結果、プログラムを実行しても a の値は変化せず、コンパイルされたプログラムは次のように出力する。 a=0, b=1

健全なマクロ 健全なマクロのシステムでは、マクロ展開プロセスにおいて処理系がすべての識別子(関数名、変数名、if、while など)のレキシカルスコープを保証し、意図しない捕捉(キャプチャー)を防ぐ。この性質は「参照透過性」と呼ばれている。捕捉(キャプチャー)が必要な場合のために、いくつかの実装では、健全なマクロのメカニズムにプログラマーが明示的に違反できるようにしている。

たとえば、Scheme の define-syntax は健全なマクロのシステムである。すなわち、マクロ展開の処理において、見かけ上同じにみえる識別子でもそれぞれが所属する文脈を加味して処理されるため、プログラムが意図通りに動作することが保証されている。以下の my-unless の実装は、期待通りの振る舞いをする:

(define-syntax my-unless (syntax-rules () [(_ condition body ...) (if (not condition) ;; 大域的な名前空間に所属する not: 論理否定を意味する本来の定義である。 (begin body ...))]))

(let ([not (lambda (b) b)]) ;; 局所的な名前空間に所属する not: 論理否定を意味する本来のものとは異なる定義である。 (my-unless #t (display "この文は出力されることはない!") (newline)))

脚注

参考文献 Paul Graham 著、野田開 訳『On Lisp: Advanced Techniques for Common Lisp』オーム社、2007年3月1日。翻訳者によるWebページ syntax-rules on schemewiki、 syntax-case on schemewiki、 examples of syntax-case on schemewiki、 syntactic closures on schemewiki、 simpler-macros on schemewiki、 examples of simpler-macros on schemewiki、 Writing Hygienic Macros in Scheme with Syntax-Case William D. Clinger; Mitchell Wand (2020-06-12). "Hygienic macro technology" (英語). Proceedings of the ACM on Programming Languages 4 (HOPL): 1–110. doi:10.1145/3386330. ISSN 2475-1421.