現状の問題としては、同一の環境でリネームされた識別子(本来はシンボル)が同名にならないというものだ。これは本質的に2つの問題を含んでいることを示唆している。
- 識別子を用いてシンボルの同一性を取ろうとしていること
- α変換が不完全である
- 識別子が本来の意味を越えて使用されているため、複雑なものが複雑怪奇にグレードアップしている
- 識別子=構文オブジェクト=パターン変数=リネームされたシンボルになっている
- 使用されている環境に応じて適切にシンボルのリネームを行う
- コンパイル時の環境
- マクロ束縛時の環境
- マクロ展開時の環境
(import (rnrs)) (let ((bv #t)) ;; ※1 (define-syntax foo (lambda (x) (syntax-case x () ((_) (with-syntax ((set (let ((type #'bytevector-u8-set!)) #`(#,type bv 0 1)))) #'(let ((bv (make-bytevector 1))) set bv)))))) (let ((bv #f)) (display (foo))))まぁ、前回とほぼ同じコードである。違いは、マクロ束縛時環境が何かを捉えている点である。ここで問題になるのは、全てのbvが同一のシンボルになる必要がある点である。そうすると、コンパイル時環境を使うのはまずいことになる。
let
で束縛されたtypeが入ってくるからだ。そうすると、二つ目のbvと環境が異なるため、生成されるシンボルが異なる。この状態では、マクロ束縛時と展開時の環境が多少違う。このパターンを考えるとマクロ束縛時の環境を使ってリネームするのが正しい気がする。ただし、リネームされたシンボルは※1と同名になる必要がある。これは、
with-syntax>で生成されたものがそれを参照している必要があるからだ。また、最終的に返される構文で束縛されるbvも同様である。それによってシャドウイングされて結果正しい値を返す。
もし、このスクリプトでマクロ展開時の環境を使うと、(まぁ、結果的には問題ないのだが)、二つ目のbvにリネームされることになるのでうれしくない。
なんとなくどうすればいいのか見えてきた。結果として以下のように修正すれば、いいのではないだろうか?
- コンパイラ側のリネームを(uninterned)シンボルを使うように修正
- マクロ展開器ではマクロ束縛時の環境を元にシンボルを(uninterned)シンボルにリネーム