Let's start Scheme

2015-08-09

pandoricマクロ

SchemeでCLのpandoricマクロを実装したらSagittariusでは動かないというのを捕捉した。コードをGistに貼り付けてもらった。これ。正直見ただけで、「あ、これ動かないやつだ」分かるのが嫌だったが、一応確認してみた。うん、動かないね・・・

一応言い訳をしておくと、R6RS版のsyntax-rulesなら動く。問題になるのはR7RS版のsyntax-rules。何度もブログに書いてるから知ってる人は知ってると思うけど、SagittariusではR7RSで追加された拡張を実現するために、R7RSのsyntax-rulesはChibi Schemeから移植したものを使っている。これは大抵の場合で上手く動くんだけど、上記のような、テンプレート変数で生成した一時変数を別ライブラリで大域に束縛すると動かなくなる。

もう少し短い例だと以下のようなコード:
;; foo.scm
(define-library (foo)
  (export defbar)
  (import (scheme base))
  (begin
    (define-syntax defbar
      (syntax-rules ()
        ((_ name)
         (defbar name t1))
        ((_ name t1)
         (begin
           (define t1 'bar)
           (define (name) t1)))))))

(import (scheme base) (scheme write) (foo))

(defbar boo)

(display (boo)) (newline)
;; -> error
#|
sash -r7 foo.scm
|#
(scheme base)(rnrs)にすると動く(define-libraryは変更する必要はない)。何が問題かといえば、defbar内で細くされたテンプレート変数は、そのマクロが定義されたライブラリを内部で保持する識別子となるため、展開後に参照しようとするとそんなものないと怒られるのである。これは健全性を保持するためにそうなっているのだが、一時変数を大域で束縛するとそれがあだになるのである。

ではどうするかなのだが、一番確実なのはsyntax-caseで実装するというものなのだが、これも実は内部的な問題がある。コンパイラ内でマクロを使っているのだが、このマクロはerで実装されたsyntax-rulesであるということに大分依存している。単純にerの方が作りが単純なので識別子が保持する環境とかをあまり考えなくて済むというだけなのではあるが。さらに、これをやるとR6RSとR7RSで使用する_...を分けなければならなくなるのであまり美味しくない。(理由はsyntax-caseにある)

次にやれそうなのは、syntax-rulesの実装を変えることなのだが、これやってもerで作ったマクロに問題が残ることになるので、近い(遠いかも)将来、erがR7RS-largeに組み込まれた際にいやな感じになる。(のでやらない)

残るは少しadhocになるが、大域に束縛された識別子の定義位置が別ライブラリだった場合に同一の識別子の位置を無理やり束縛されるライブラリに変更するとかになる。ぱっと思いつくパターンでは動くような気がするんだけど、これだけadhocな変更だと大体どこかでほころびが出ることが過去の経験から分かっているのでやるなら思いつく限りのパターンをテストしないとという感じになる。(まぁ、似たようなことをsyntax-caseでやっているので動くとは思うんだけど、どうだろう?)

ちと考える必要があるなぁ・・・

No comments:

Post a Comment