現状の問題としては、同一の環境でリネームされた識別子(本来はシンボル)が同名にならないというものだ。これは本質的に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)シンボルにリネーム
いろいそうだうだ書いたが結局まったく別の方法で解決することにした。それは、VMにもう一個マクロ用のフィールドを持たせて、リネームしたシンボルを記憶させておくというもの。
なぜそうしたかを忘れないようにメモ。
- 上記の方法ではuninternedシンボルの比較方法がない
- そもそも、internされてないシンボルはeq?での比較ができない
- かといって、全てのシンボルをinternしたくない
- シンボルテーブルの肥大化は避けたいところ(省メモリの一環)
- この問題とその他いくつかのマクロ周りの問題は、小手先ではなくマクロ展開器を割りと根本から見直す必要がある
- 大掛かりに直すとなると時間がかなりかかるので、現状は避けたい
- それに、方針を固めないとまた暗礁に乗り上げるだけだ
No comments:
Post a Comment