Let's start Scheme

2012-09-11

syntax-rules

マクロ周りは非常に奥が深くて僕では溺れてしまうという話。
昨日の記事を書いた後(しばらくは冪剰余やってたけど)気づいた。そういえば、Sagittariusではsyntax-rulesを使ってdummyみたいなことは出来ないと。
ということで、テスト。
;;(import (rnrs))
;;(import (scheme))
(define-syntax test
  (syntax-rules ()
    ((_ var val)
     (begin
       (define dummy val)
       (define (var) dummy)))))

(test a 'a)
(test b 'b)
(display (a)) (newline)
(display (b)) (newline)
最初のコメントは処理系に応じて入れたり消したり。試した処理系。
  • R5RS Gauche、Racket(plt-r5rs)
  • R6RS Ypsilon、Mosh、Petite Chez Scheme、Sagittarius(R6RS?)
  • R7RS Chibi-Scheme(一応そう謳ってるし)
とりあえず、ほしい結果は多分以下。
#| R6RS的にはこうだよね?
a
b
|#
#| 字面的にはこうでもよさげ?(2)
b
b
|#
dummyというシンボルを使いまわしているように見えるので、(2)でもいいような気がしないでもない。(R6RSまじめに読んでない、R5RSは目を通したことすらない気がする・・・)
結論を言えば、Racket、Ypsilon、Mosh、Petite Chez Schemeは最初の結果、残り(Gauche、Chibi及びSagittarius)は2番目になった。

分かっていたんだけどね、こうなるって(Sagittariusの話)。そして、これは現状のつくりを維持するなら正直直しようがない。原因は2番目の結果になった処理系は、dummyを参照することができるということ。Sagittariusは確定なんだけど、恐らく残りの2つもマクロ展開時にシンタックスの情報を持っていない。なので、dummyとdefineの区別がつけられず、シンボルのrename(もしくはunintern)を行えない。

単純な話、上記の例で言えば、2つのdummyは同じrenameされたシンボルに変換できる。そうすると、トップレベルではdummyというシンボルは何かしらにrenameされているので見えないが、varが束縛(捕捉?)したdummyは同一のものにrenameされているのでそこからは見える。

これ多分、明示的にマクロ展開のフェーズを持たないと無理じゃないかな?もしくは、マクロ展開器がもっといろいろ知っている必要がある。あぁ、でもそれくらいならいけるかな?

結局、テンプレートをre-writeする際に、それがグローバルに束縛されているならそのままで、そうじゃないものならrenameとかすればいいかも。(でも、やるなら0.3.7以降だな。0.3.6はリリースが近いから無理だ)

これって、R5RSとR6RSで明確に定義されている非互換な動作なのだろうか?

2 comments:

齊藤 said...

R5RS にはトップレベルでは未定義と明記されています。 が、その意味付けはちょっと曖昧な気がするので、↓ここでの議論を参考にすると良いと思います。
http://practical-scheme.net/wiliki/wiliki.cgi?Scheme%3a%E5%88%9D%E5%BF%83%E8%80%85%E3%81%AE%E8%B3%AA%E5%95%8F%E7%AE%B1%3alog00#H-1pp9yun
R6RS の解釈は私の理解が正しいかどうか自信はないのですが基本的にトップレベルとその他の body は区別が無いので、リネームされるのは必須だと思います。

kei said...

R5RSは未定義なんですね。ということは、非互換な変更ではなく、単に明確化されただけと・・・

R6RS内だったか記憶がないのですが、どこかでsyntax-rules内で定義された値が一時変数的に使えるのをみたので、多分必須なんだと思っています。(自信0)

Post a Comment