R6RS:翻訳:Standard Libraries:12.8 Derived forms and procedures
12.8 派生式形と手続
本節で述べるフォームと手続きは本章の先の節で述べたフォームと手続きで定義することができる。
[syntax] (with-syntax ((<pattern> <expression>) ...) <body>)
with-syntax フォームは、変数を束縛の let と同様に、パターン変数を束縛に使う。 これをつかうと変換子中で出力を別個につくってまとめることができる。
各 <pattern> は syntax-case のパターン部分にあたる。各 <expression> の値は計算されると対応する <pattern> にしたがって分解され、syntax-case の場合と同様 <pattern> 中のパターン変数が <body> 中の対応する部分に束縛される。
with-syntax は syntax-case をつかうと次のように定義できる。
(define-syntax with-syntax
(lambda (x)
(syntax-case x ()
((_ ((p e0) ...) e1 e2 ...)
(syntax (syntax-case (list e0 ...) ()
((p ...) (let () e1 e2 ...))))))))
下の cond の定義例では with-syntax をつかって、内部的に再帰をつかい出力を構築する変換子をつくる。 ここでは、cond の節のすべての場合をあつかい、可能ならば one-armed if を生成するようにしている。
(define-syntax cond
(lambda (x)
(syntax-case x ()
[(_ c1 c2 ...)
(let f ([c1 #’c1] [c2* #’(c2 ...)])
(syntax-case c2* ()
[()
(syntax-case c1 (else =>)
[(else e1 e2 ...) #’(begin e1 e2 ...)]
[(e0) #’e0]
[(e0 => e1)
#’(let ([t e0]) (if t (e1 t)))]
[(e0 e1 e2 ...)
#’(if e0 (begin e1 e2 ...))])]
[(c2 c3 ...)
(with-syntax ([rest (f #’c2 #’(c3 ...))])
(syntax-case c1 (=>)
[(e0) #’(let ([t e0]) (if t t rest))]
[(e0 => e1)
#’(let ([t e0]) (if t (e1 t) rest))]
[(e0 e1 e2 ...)
#’(if e0
(begin e1 e2 ...)
rest)]))]))])))
[syntax] (quasisyntax <template>)
[auxiliary syntax] unsyntax
[auxiliary syntax] unsyntax-splicing
quasisyntax は syntax とほぼ同等であるが、quasiquote の場合と同様に(R6RS:翻訳:R6RS:Quasiquotation?)、 quote されたテキストの一部を評価することができる。
quasisyntax テンプレートの内部では、 unsyntax、 unsyntax-splicing の下位フォームだけが評価され、その他の部分はすべて、 syntax の場合と同様にふつうのテンプレートとして扱われる。各 unsyntax の下位フォームは unsyntax フォームに代わり挿入される。それに対して unsyntax-splicing の下位フォームは周囲のリストやベクタ構造に挿入・接合される。 unsyntax と unsyntax-splicing が使えるのは quasisyntax 式の内部だけである。
quasisyntax 式は入れ子にすることができる。各 quasisyntax は新たな syntax quotation を一段階導入し、 unsyntax や unsyntax-splicing は導入された syntax quotation を一段階取り除く。 n 個の quasisyntax 式内に入れ子になっている式を評価するには、それが n 個の unsyntax や unsyntax-splicing の内部になければならない。
報告書の「略記法」の節(R6RS:翻訳:R6RS:Lexical syntax?)で述べたように、 #`<template> は (quasisyntax <template>) と等価であり、#,<template> は (unsyntax <template>) と等価で、 #,@<template> は (unsytax-splicing <template>) と等価である。
quasisyntax キーワードは、多くの場合に with-syntax の代わりに使うことができる。例えば、上で with-syntax を使って示した case の定義は、下のように quasisyntax を使って書き直すことができる。
(define-syntax case
(lambda (x)
(syntax-case x ()
[(_ e c1 c2 ...)
#‘(let ([t e])
#,(let f ([c1 #’c1] [cmore #’(c2 ...)])
(if (null? cmore)
(syntax-case c1 (else)
[(else e1 e2 ...)
#’(begin e1 e2 ...)]
[((k ...) e1 e2 ...)
#’(if (memv t ’(k ...))
(begin e1 e2 ...))])
(syntax-case c1 ()
[((k ...) e1 e2 ...)
#‘(if (memv t ’(k ...))
(begin e1 e2 ...)
#,(f (car cmore)
(cdr cmore)))]))))])))
0 個ないし 2 個以上の下位フォームをともなう unsyntax や unsyntax-splicing は(リストやベクタに)接合を行う文脈でだけ有効である。 (unsyntax template ...) は (unsyntax template) ... と等価であり、(unsyntax-splicing template ...) は (unsyntax-splicing template) ... と等価である。このようなフォームはひとつに、 quasisyntax 展開器の出力の中間形式として有用である。
注: unsyntax や unsyntax-splicing を 0 個ないし 2 個以上の下位フォームとともに使うと、ある種のイディオムが使えるようになる [2]。例えば #,@#,@ がそれである。これは、二重に入れ子になり、二重に評価される quasisyntax 式の中で使われたときに二重に継ぎ合わせる効果を持つことになる(これは R6RS:翻訳:R6RS:Quasiquotation? の節で示した、入れ子の quasiquote の例と同じである)。
任意の syntax-rules フォームは lambda 式と syntax 式を使って syntax-case で定義することができる。また、syntax-rules は次のように syntax-case を使って定義することもできる。
(define-syntax syntax-rules
(lambda (x)
(syntax-case x ()
[(_ (lit ...) [(k . p) t] ...)
(for-all identifier? #’(lit ... k ...))
#’(lambda (x)
(syntax-case x (lit ...)
[(_ . p) #’t] ...))])))
注: 基本ライブラリ の identifier-syntax フォーム(R6RS:翻訳:R6RS:Macro transformers? 参照)は syntax-case、 syntax、 make-variable-transformer を使って次のように定義することができる。
(define-syntax identifier-syntax
(syntax-rules (set!)
[(_ e)
(lambda (x)
(syntax-case x ()
[id (identifier? #’id) #’e]
[(_ x (... ...)) #’(e x (... ...))]))]
[(_ (id exp1) ((set! var val) exp2))
(and (identifier? #’id) (identifier? #’var))
(make-variable-transformer
(lambda (x)
(syntax-case x (set!)
[(set! var val) #’exp2]
[(id x (... ...)) #’(exp1 x (... ...))]
[id (identifier? #’id) #’exp1])))]))