R6RS:翻訳:R6RS:10 Expansion process
10 章 展開処理
マクロの使用(R6RS:翻訳:R6RS:9.2 Macros 参照)は評価の最初の段階で構文展開器によって核形式に展開される。核となるフォームの集合は実装系依存であり、これらの展開器の出力中でのこれらのフォームの表現も実装系依存である。展開器が構文抽象に遭遇すると、構文抽象を展開するために対応する変換子が起動され、変換子の返したフォームに対して展開が繰り返される。展開器が核形式に遭遇した場合には、その下位フォームが式文脈や定義文脈にあれば、再帰的にそれを展開し、展開された下位フォームからフォームを再構築する。変数やキーワードの字句有効範囲を実現するために識別子の束縛情報は展開中も保持される。
定義を扱うために、展開器は <body> (R6RS:翻訳:R6RS:11.3 Bodies 参照)や <library body> (R6RS:翻訳:R6RS:7 Libraries 参照)の最初のフォームを左から右へ処理して行く。遭遇したフォームを展開器がどのように処理するかはそのフォームの種類に依存している。
- マクロの使用
展開器はマクロの使用を変換するために対応する変換子を起動し、結果のフォームに対してこれらの動作のいずれかのうち、適切なものを再帰的に行なう。
- define-syntax フォーム
展開器は右辺式を展開・評価し、キーワードを結果の変換子に束縛する。
- define フォーム
展開器は定義された識別子が変数であった事実を記録するが、右辺式の展開はすべての定義が処理されるまで延期される。
- begin フォーム
展開器は下位フォームを処理中の本体フォームに継ぎ合わせる(R6RS:翻訳:R6RS:11.4 Expressions の 11.4.7 参照)。
- let-syntax ないしは letrec-syntax フォーム
展開器は、 let-syntax と letrec-syntax によって束縛されたキーワードが内側の本体部分フォームだけで可視になるようにし、内側の本体部分を処理中の(外側の)本体部分フォームに継ぎ合わせる。
- 式(定義以外)
展開器は延期されていた右辺式と本体中の現在の式と残りの式の展開を完了し、定義された変数、展開された右辺式、展開された本体部分から letrec* フォームと等価なものを作成する。
変数定義の右辺についてはすべての定義を見終るまで展開は延期される。したがって、右辺中のキーワードや変数への参照は、もしあれば局所束縛に解決される。
フォームの並びの中で、定義はその束縛が定義の延期されていない部分や並びのうちで先行する定義の意味を決定するような識別子を定義してはならない。例えば、次の式の本体部分はこの制約に違反している。
(let ()
(define define 17)
(list define))
(let-syntax ([def0 (syntax-rules ()
[(_ x) (define x 0)])])
(let ([z 3])
(def0 z)
(define def0 list)
(list z)))
(let ()
(define-syntax foo
(lambda (e)
(+ 1 2)))
(define + 2)
(foo))
次のものはこの制約に違反していない。
(let ([x 5])
(define lambda list)
(lambda x x)) ⇒ (5 5)
(let-syntax ([def0 (syntax-rules ()
[(_ x) (define x 0)])])
(let ([z 3])
(define def0 list)
(def0 z)
(list z))) ⇒ (3)
(let ()
(define-syntax foo
(lambda (e)
(let ([+ -]) (+ 1 2))))
(define + 2)
(foo)) ⇒ -1
実装系はこの制約への違反を構文違反として扱うべきである。
このアルゴリズムはフォームを直接には再処理しないことに注意。定義に左から右への単一のパスに本体式と延期された右辺(任意の順序で)の単一パスが続いている必要がある。
例:
(lambda (x)
(define-syntax defun
(syntax-rules ()
[(_ x a e) (define x (lambda a e))]))
(defun even? (n) (or (= n 0) (odd? (- n 1))))
(define-syntax odd?
(syntax-rules () [(_ n) (not (even? n))]))
(odd? (if (odd? x) (* x x) x)))
(lambda (x)
(letrec* ([even?
(lambda (n)
(or (= n 0)
(not (even? (- n 1)))))])
(not (even? (if (not (even? x)) (* x x) x)))))
ただし、出力の構造は実装系依存である。
<top-level body> では定義と式が交互に現れることができるため(R6RS:翻訳:R6RS:8 Top-level programs 参照)、 展開器の <top-level body> の処理はいくらかより複雑である。 <body> や <library body> に対しては次の例外を含みつつ上で述べたように振舞う。展開器が定義でないものを見つけると、その展開を延期し、定義の探索を続ける。フォームの集合の終端にたどりつくと、定義された変数と、展開された右辺式、展開された本体式と等価な letrec* フォームを生成する。各本体式の、本体部分で変数定義の前に現れた <expression> について、 letrec* の束縛の対応する場所にダミーの束縛を、左辺に新しい一時変数と (begin <expression> <unspecified>) とともに作成する。ここで、 <unspecified> は副作用のない未規定値を返す式である。右辺では、左から右へという評価順序が保存される。 begin で包むのは <expression> が任意の個数の値を返せるようにするためである。