構文パラメータは、マクロ展開時に動的に構文束縛を置き換えるものです。
束縛フォームは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 …を実行します。 この再束縛はマクロ展開時の動的スコープで有効です (実行時の動的スコープとは関係ないことに注意してください)。