2011-04-28

letrec-syntaxにはまる

現在Racketのr6rs test suiteをパスしようとしているのだが、
(実は既に絶対にパスしないテストがあったり、テストライブラリをいじらないとだめだったりとかなので、すでのR6RSとは名のれないというのは確定なのだが)、
こんな初歩的なテストに引っかかってる。
(letrec-syntax ((my-or (syntax-rules ()
                         ((my-or) #f)
                         ((my-or e) e)
                         ((my-or e1 e2 ...)
                          (let ((temp e1))
                            (if temp
                                temp
                                (my-or e2 ...)))))))
  ;; 展開後ifが関数呼び出しに、マクロ内のtempが
  ;; すべてletで束縛しているものになる
  (let ((x #f)
        (y 7)
        (temp 8)
        (let odd?)
        (if even?))
    (my-or x
           (let temp)
           (if y)
           y)))
何がだめかというとsyntax-rulesの健全性がletrec-syntax内では崩れているということ。
逆に言えば、letrec-syntaxであればsyntax-rulesを使って伝統的マクロみたいなことができる、のだがまぁそれではだめなわけで。

理由は実は分かっている。
syntax-rulesはexplicit renamingで実装しているのだが、そのrenameが問題になっている。どういうことかというと、renameにはコンパイル時環境を使用してシンボルを識別子に変換しているのだが、その環境が問題になっている。letで束縛した束縛変数の情報がコンパイル時環境の中に含まれているため、マクロ展開時にシンボルの探索を行うと束縛変数が引っかかってくるのである。
っで、renameでは渡された引数がシンボルだった場合、現時点での環境を使用して識別子を生成するためおかしなことになるのである。

じゃあ、どう解決するか。
あんまりいい案がないのだが、syntax-rulesはパターン変数以外は外部と干渉しないことが分かっているので、パターンの中にあるパターン変数以外のシンボルをあらかじめ環境の中に入れてしまえばとりあえず回避できそうな気がする。
問題は、マクロ変換器がコンパイル時環境を直接触れないことか。
さてどうしたものか。

No comments:

Post a Comment