Syntax parameters16 are a
mechanism for rebinding a macro definition within the dynamic extent of
a macro expansion. This provides a convenient solution to one of the
most common types of unhygienic macro: those that introduce a unhygienic
binding each time the macro is used. Examples include a lambda
form with a return
keyword, or class macros that introduce a
special self
binding.
With syntax parameters, instead of introducing the binding unhygienically each time, we instead create one binding for the keyword, which we can then adjust later when we want the keyword to have a different meaning. As no new bindings are introduced, hygiene is preserved. This is similar to the dynamic binding mechanisms we have at run-time (see parameters), except that the dynamic binding only occurs during macro expansion. The code after macro expansion remains lexically scoped.
Binds keyword to the value obtained by evaluating
transformer. The transformer provides the default expansion
for the syntax parameter, and in the absence of
syntax-parameterize
, is functionally equivalent to
define-syntax
. Usually, you will just want to have the
transformer throw a syntax error indicating that the keyword
is supposed to be used in conjunction with another macro, for example:
(define-syntax-parameter return (lambda (stx) (syntax-violation 'return "return used outside of a lambda^" stx)))
Adjusts keyword … to use the values obtained by evaluating
their transformer …, in the expansion of the exp
… forms. Each keyword must be bound to a syntax-parameter.
syntax-parameterize
differs from let-syntax
, in that the
binding is not shadowed, but adjusted, and so uses of the keyword in the
expansion of exp … use the new transformers. This is
somewhat similar to how parameterize
adjusts the values of
regular parameters, rather than creating new bindings.
(define-syntax lambda^ (syntax-rules () [(lambda^ argument-list body body* ...) (lambda argument-list (call-with-current-continuation (lambda (escape) ;; In the body we adjust the 'return' keyword so that calls ;; to 'return' are replaced with calls to the escape ;; continuation. (syntax-parameterize ([return (syntax-rules () [(return vals (... ...)) (escape vals (... ...))])]) body body* ...))))])) ;; Now we can write functions that return early. Here, 'product' will ;; return immediately if it sees any 0 element. (define product (lambda^ (list) (fold (lambda (n o) (if (zero? n) (return 0) (* n o))) 1 list)))
Described in the paper Keeping it Clean with Syntax Parameters by Barzilay, Culpepper and Flatt.