構文パラメータは、マクロ展開時に動的に構文束縛を置き換えるものです。
束縛フォームはparameterizeに似ています(パラメータ参照)が、
マクロ展開時のみに作用します。
これを使うと、他のマクロに影響を与えるマクロを衛生的に書くことができます。
構文パラメータはSRFI-139で定義されました。
例えば、(block <body> ...)というマクロを考えてみます。
これは<body>を順番に実行します。<body>の中で、
別のマクロ(return <expr>)を呼び出すことができて、
その場合、<expr>が評価され、それを値として直ちに(block ...)から
脱出します。
言ってみれば、マクロblockはマクロreturnの振る舞いを変える必要がある
わけです。これは次のように書くことができるでしょう。
(define-syntax block
(er-macro-transformer
(^[f r c]
(quasirename r
`(let/cc ,'return ,@(cdr f)))))
(block (print 1) (print 2) (return 'oops) (print 4))
⇒ print 1, 2, and then returns oops
ただ、この定義ではreturnを非衛生的に挿入する必要があることに注目してください。
これは、returnを別の目的で使いたい他のライブラリがあった場合、
一緒に使うと衝突してうまくいかないかもしれません。
構文パラメータを使うと、衛生的に書くことができます:
(define-syntax-parameter return
(syntax-rules ()
((_ . _) (syntax-error "return outside block"))))
(define-syntax block
(syntax-rules ()
((_ body ...)
(let/cc %return
(syntax-parameterize ((return (syntax-rules ()
((_ expr) (%return expr)))))
body ...)))))
blockの中のreturnは、define-syntax-parameterで
定義されたreturnと同じ束縛を参照している時のみ効果を持ちます。
例えば上記コードをライブラリにして、ユーザがプレフィックスつきでそれを
インポートしたとしましょう。
その場合、非局所的脱出をするreturnをプレフィクスつきになり、
そのモジュールで定義される別のreturnと共存できます。
(use block :prefix b:) (define return ...) (b:block ... (return ...) ; 現在のモジュールの 'return' を参照 ... (b:return 'oops) ; blockから抜ける return の呼び出し ... )
[SRFI-139]
define-syntaxと同様に、transformer-specで作られるマクロを
nameに束縛します。それに加えて、束縛が構文パラメータであることが記録され、
syntax-parameterizeで置き換えることができるようになります。
与えられたtransformer-specは、nameが
syntax-parameterizeで再束縛されていない場合のデフォルトのマクロとして
動作します。transformer-specに使える形式は
define-syntaxのものと同じです。
[SRFI-139] keyは構文パラメタに束縛された識別子でなければなりません。 このフォームは、keyの構文束縛をtransformer-specで指定されるものに 置き換えて、body …を実行します。 この再束縛はマクロ展開時の動的スコープで有効です (実行時の動的スコープとは関係ないことに注意してください)。