For Development HEAD DRAFTSearch (procedure/syntax/module):

5.6 識別子

衛生的マクロの説明では、衝突を避けるためにシンボルを「リネームする」といいますが、 本当に名前を変えたシンボルを作っているわけではありません。後でスコープの解決をする際に、 リネームされたシンボルと元のシンボルを関連付ける必要があるのですが、 それを別のテーブルで管理するのは重いのです。 そこで、er-macro-expanderに渡されるリネーム手続きは、 実際には元のシンボルを構文情報で「包む」ということをしています。

マクロの中身をいじっていると、しばしば #<identifier user#foo.fb4ca828> のように印字されるオブジェクトを見るでしょう。 それが包まれたシンボルです。

マクロ展開結果がさらに他のマクロ展開器に渡された場合は、 一旦包まれたシンボルがさらに包まれることもあります。

マクロ展開器は、入力に含まれているシンボルが既に包まれていることを想定しなければなりません。 「シンボル」ではなく「識別子」と呼んでいるのはそのためです。 識別子は、プログラム中で変数か構文キーワードとして働くもので、 その実体はシンボルか、包まれた識別子です。 (クオートの中にあるシンボルは常に「裸の」シンボルになります。 quoteが構文情報を剥ぎ取るからです)。

伝統的なLispのマクロではよく入力フォーム中のシンボルを調べますが、 Schemeでは入力を識別子やその他のオブジェクトからなる木と考えます。 伝統的なLispマクロでsymbol?を使うところでは、 identifier?を使ってオブジェクトが識別子かどうか調べる必要があります。 また、識別子同士が等しい(同じ束縛を表している)かどうかを調べるには単なるeq?ではなく、 er-macro-transformerに与えられる “compare” 手続きや、 free-identifier=?といった特別な手続きを使う必要があります。

Builtin Class: <identifier>

構文情報で包まれた識別子を表すクラスです。衛生的マクロ展開器の “rename”手続きにより作られます。

包まれた識別子は、プログラムの構文情報を含んでおり、 ポータブルな形でセーブすることはできません。その情報はマクロ展開の期間内のみで有効です。

識別子について詳しくは識別子を参照してください。

Function: identifier? obj

objがシンボルもしくは包まれた識別子である場合に#tを、 そうでなければ#fを返します。

R6RSでは、identifier?はシンボルとdisjointな型を持つ「識別子オブジェクト」 についてのみ#tを返す、と規定されています。 オブジェクトがシンボルでない識別子であることを確かめたい場合は、 下のwrapped-identifier?が使えます。

Function: wrapped-identifier? obj

objが包まれた識別子なら#tを、そうでなければ#fを返します。

R6RSのidentifier?に相当します。

Function: identifier->symbol obj

objはシンボルか包まれた識別子でなければなりません。objの元となった シンボルを返します。

Function: free-identifier=? id1 id2

id1id2がともに包まれた識別子で、次のいずれかの場合にのみ#tを 返します: (1)id1id2が同じ束縛を表しているか、 (2)id1id2がどちらも束縛されていない。

id1id2の少なくとも一方が包まれた識別子でなければ、この手続きは #fを返します。「裸の」シンボルはレキシカル情報を欠いているため、この手続きでは 比較することができません。シンボルをレキシカルな情報で包むには、 er-macro変換器に渡されるrename手続きを通す必要があります。

er-macroを使っている場合、compare手続きで識別子の比較ができるので、 この手続きを直接使う必要はあまりないでしょう。

Function: unwrap-syntax form

formの中に含まれる「包まれた識別子」の構文情報を全て剥ぎ取った形での formのコピーを返します。 マクロ展開器の出力には包まれた識別子が含まれていてとても見づらいです。 この手続きはformをトラバースし、包まれた識別子があればそれを identifier->symbolを使って元のシンボルへと戻します。

返り値は通常のS式で読みやすいですが、構文情報が失われていることに注意してください。 例えば本来異なるはずの識別子がたまたま同じ名前を持っていると、区別ができなくなります (syntax-rulesを再帰的に呼び出して一時変数を作るパターンでは そういうことが頻繁に起きます)。 新たに挿入された同名の識別子を区別したい場合は、unravel-syntaxを使ってください。

Function: unravel-syntax form

form中に含まれる包まれた識別子を裸のシンボルに変換した形での formのコピーを返しますが、同じ名前を持つ異なる識別子があった場合は 区別できるようにサフィックスをつけた名前に変更します。

syntax-rulesを再帰的に呼び出して一時変数を生成する定石では、 新たに作られる変数は全て同じ名前になってしまいますが、それぞれ作られるタイミングが 違うために異なる識別子として扱われます。その結果をunwrap-syntaxに渡して 構文情報を剥ぎ取ると、それらの識別子同士の区別ができなくなってしまいます。

この手続きはいくつかのマクロを扱うユーティリティ関数内で自動的に呼ばれます。 unravel-syntaxの出力例は、 マクロ展開をトレースするマクロを自分で展開するを 参照してください。

ただし、グローバルな識別子は元のシンボルへと変換されるので、 どのモジュールの束縛を指しているかの情報は失われます。



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT