何度か取り上げたような気がするが、 Scheme のマクロ機構における datum->syntax の第一引数の意味が未だによくわからない。 あらためて規格を読んでみることにした。
(datum->syntax template-id datum)
template-idはテンプレート識別子であり、datumはデータ値でなければならない。 この手続きはtemplate-idと同一の文脈情報をもつdatumの構文オブジェクト表現を返す。 このとき、この構文オブジェクトはtemplate-idが挿入されたのと同時にコードに挿入されたかのようにあつかわれる。
ここで「文脈情報 (contextual information)」という言葉がよくわからない。
更に続きの文を見ると…
datum->syntaxをつかうと、その識別子がもともと入力にあったかのようにあつかわれる暗黙の識別子をつくり、静的スコープの規則を「曲げる」ことができるようになる。 すなわち、入力フォームに明示的にあらわれなかった識別子に対して参照可能な束縛や参照を挿入する構文抽象を定義することができるのである。
とあることから、私はおぼろげに文脈情報というのはスコープかそれに密接に関係する何かであろうと理解しているのだが、スコープの規則を曲げるという感覚がイマイチつかめていない。
R6RS にある例はかなり単純なものでしかなく、意味するところがどうにも読み取れない。
そこで、いかにもスコープを曲げたという感じのするマクロを実際に書いてみることにした。 動作確認は Ypsilon と Mosh と Petite Chez Scheme で行っている。 (にちゃんねるの「Lisp Scheme Part27」スレッドに先日書いたものを少し変更したものである。)
(define-syntax let/scope
(lambda(x)
(syntax-case x ()
((k scope-name body ...)
#'(let-syntax
((scope-name
(lambda(x)
(syntax-case x ()
((_ b (... ...))
#`(begin
#,@(datum->syntax #'k
(syntax->datum #'(b (... ...))))))))))
body ...)))))
使い方は以下のような感じだ。
(let ((x 1))
(let/scope d1
(let ((x 2))
(let/scope d2
(let ((x 3))
(list (d1 x) (d2 x) x)))))) ;; ⇒ (1 2 3)
通常はシャドウイングされてしまう変数 x をスコープの名前を指定することでそれぞれのスコープにおいての変数 x として解決する。
Document ID: c1841d32bcd7a897a3d1b4073b2a67b4
0 件のコメント:
コメントを投稿