だから...私はスキームr6rsに不慣れで、マクロを学んでいます。誰かが「衛生」とはどういう意味かを私に説明できますか?
前もって感謝します。
衛生は、マクロのコンテキストでよく使用されます。衛生的なマクロは、展開中のコードに干渉するリスクのある変数名を使用しません。これが例です。or
マクロを使用して特殊なフォームを定義するとします。直感的に、
(or a b c ... d)
のようなものに拡張されます(let ((tmp a)) (if tmp a (or b c ... d)))
。(or)
(簡単にするために、空のケースは省略しています。)
さて、tmp
上記のスケッチ展開のように名前が実際にコードに追加された場合、同じ名前の別の変数に干渉する可能性があるため、衛生的ではなく、悪い結果になります。言う、私たちは評価したかった
(let ((tmp 1)) (or #f tmp))
直感的な拡張を使用すると、これは次のようになります
(let ((tmp 1)) (let ((tmp #f)) (if tmp (or tmp)))
tmp
fromマクロは最も外側のをシャドウイングするためtmp
、結果は#f
の代わりになり1
ます。
ここで、マクロが衛生的である場合(Schemeでは、を使用すると自動的に適用されますsyntax-rules
)、展開に名前を使用する代わりにtmp
、コード内の他の場所に表示されないことが保証されているシンボルを使用します。CommonLispで使用できますgensym
。
PaulGrahamのOnLispは、マクロに関する高度な資料を持っています。
マクロが使用される場所に単純に展開されることを想像すると、マクロで変数を使用する場合、そのマクロが使用される場所にすでに変数が定義されている可能性があることも想像できa
ます。a
これはあなたが望むものではありませa
ん!
このようなことが起こらないマクロシステムは、衛生と呼ばれます。
この問題に対処するにはいくつかの方法があります。1つの方法は、マクロで非常に長く、非常に不可解で、非常に予測不可能な変数名を使用することです。
これのもう少し洗練されたバージョンは、gensym
他のいくつかのマクロシステムで使用されるアプローチです。プログラマーが非常に長く、非常に不可解で、非常に予測不可能な変数名を思い付く代わりに、非常にgensym
長い、非常に長いを生成する関数を呼び出すことができます。不可解で、非常に予測不可能で、一意の変数名。
そして、私が言ったように、衛生的なマクロシステムでは、そもそもそのような衝突は起こり得ません。マクロシステムを衛生的にする方法はそれ自体が興味深い質問であり、Schemeコミュニティはこの質問に数十年を費やしており、それを行うためのより良い方法を考え続けています。
この言語がまだ使われていることを知ってとてもうれしいです!衛生的なコードは、(マクロを介して)挿入されたときに既存の変数との競合を引き起こさないコードです。
ウィキペディアにはこれに関する多くの良い情報があります:http://en.wikipedia.org/wiki/Hygienic_macro
これが私が見つけたものです。それが何を意味するのかを説明することは、まったく別の問題です!
http://www.r6rs.org/final/html/r6rs-lib/r6rs-lib-ZH-1.html#node_toc_node_sec_12.1
マクロはコードを変換します。1ビットのコードを取得して、それを別のコードに変換します。その変換の一部として、それらはそのコードをより多くのコードで囲む場合があります。元のコードが変数a
を参照し、その周りに追加されたコードがの新しいバージョンを定義している場合a
、間違ったアクセスを行うため、元のコードは期待どおりに機能しませんa
:if
(myfunc a)
は元のコードでありa
、整数であると想定されており、マクロはそれを取得して次のようX
に変換します。
(let ((a nil)) X)
その後、マクロは正常に動作します
(myfunc b)
しかし、(myfunc a)
に変換されます
(let ((a nil)) (myfunc a))
期待する整数ではなくmyfunc
に適用されるため、これは機能しません。nil
衛生的なマクロは、使用される名前が一意であることを確認することにより、間違った変数がアクセスされるというこの問題(およびその逆の同様の問題)を回避します。
ウィキペディアには、衛生的なマクロについての優れた説明があります。
言及されたすべてのものとは別に、Schemeの衛生的マクロには、字句スコープに続く重要なものが1つあります。
私たちが持っていると言う:
(syntax-rules () ((_ a b) (+ a b)))
マクロの一部として、確かに+を挿入します。また、+がすでに存在する場合にも挿入しますが、。と同じ意味を持つ別の記号を挿入し+
ます。シンボルを、syntax-rules
それが適用される場所ではなく、嘘が存在する字句環境で持っていた値にバインドします。結局、字句スコープが適用されます。+
ほとんどの場合、そこに完全に新しいシンボルが挿入されますが、マクロが定義されている場所と同じ意味にグローバルにバインドされているシンボルです。これは、次のような構成を使用する場合に最も便利です。
(let ((+ *))
; piece of code that is transformed
)
したがって、マクロの作成者またはユーザーは、その使用がうまくいくことを保証することに専念する必要はありません。