139: Syntax parameters

by Marc Nieper-Wißkirchen

Status

This SRFI is currently in final status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to srfi-139@nospamsrfi.schemers.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.

Abstract

Syntax parameters are to the expansion process of a Scheme program what parameters are to the evaluation process of a Scheme program. They allow hygienic implementation of syntactic forms that would otherwise introduce implicit identifiers unhygienically.

Rationale

Syntax parameters 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, class macros that introduce a special self binding, or a while loop form that provides an escape procedure as a binding for an auxiliary abort keyword.

With the syntax-rules macro system, it is not possible to introduce bindings unhygienically. With low-level macro systems like the sc-macro-transformer or the syntax-case macro system, one can break hygiene by constructing new identifiers in an existing syntactic closure. While use cases of introducing identifiers unhygienically like in SRFI 99 seem unproblematic, the use cases given above where bindings are based on identifiers that are completely independent of the arguments of the macro are often severely broken.

With syntax parameters, instead of introducing the binding unhygienically each time, one instead creates one binding for the keyword, which is then adjusted later when the keyword to supposed to have a different meaning. As no new bindings are introduced, hygiene is preserved. This is similar to the dynamic binding mechanisms available at run-time (in the form of parameters), except that the dynamic binding only occurs during macro expansion. The code after macro expansion remains lexically scoped.

Examples

The first example is certainly not an example of good functional programming style but it easily demonstrates how syntax parameters are used and why call-with-current-continuation is known as the fundamental control-flow construct.

 (import (scheme base)
         (scheme write)
         (srfi 139))

 (define-syntax-parameter abort
   (syntax-rules ()
     ((_ . _)
      (syntax-error "abort used outside of a loop"))))

 (define-syntax forever
   (syntax-rules ()
     ((forever body1 body2 ...)
      (call-with-current-continuation
       (lambda (escape)
  	 (syntax-parameterize
	     ((abort
	      (syntax-rules ()
	        ((abort value (... ...))
	 	 (escape value (... ...))))))
	   (let loop ()
 	     body1 body2 ... (loop))))))))

 (define i 0)
 (forever
   (display i)
   (newline)
   (set! i (+ 1 i))
   (when (= i 10)
     (abort)))

The following example is derived from an example given in the Guile reference manual.

 (import (scheme base)
         (scheme write)
         (srfi 1)
         (srfi 139))

 (define-syntax-parameter return
   (syntax-rules ()
     ((_ . _)
      (syntax-error "return used outside of a lambda^"))))

 (define-syntax lambda^
   (syntax-rules ()
     ((lambda^ formals body1 body2 ...)
      (lambda formals
        (call-with-current-continuation
         (lambda (escape)
          (syntax-parameterize
              ((return
                (syntax-rules ()
                  ((return value (... ...))
                   (escape value (... ...))))))
            body1 body2 ...)))))))

 (define product
   (lambda^ (list)
     (fold (lambda (n o)
             (if (zero? n)
                 (return 0)
                 (* n o)))
           1 list)))

 ...

Specification

Syntax

 (define-syntax-parameter <keyword> <transformer spec>)

Binds <keyword> to the transformer obtained by evaluating <transformer spec>. The transformer provides the default expansion for the syntax parameter, and in the absence of syntax-parameterize, is functionally equivalent to define-syntax. (For example, you may want to have the transformer throw a syntax error indicating that the keyword is supposed to be used in conjunction with another macro as in the examples above).

 (syntax-parameterize
     ((<keyword> <transformer spec>) ...)
   <body>)

Adjusts the <keyword>s to use the transformer obtained by evaluating the corresponding <transformer>s in the expansion of the <body>. 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 <body> use the new transformers. This is somewhat similar to how parameterize adjusts the values of regular parameters, rather than creating new bindings.

Implementation

A sample implementation is provided by the Unsyntax Scheme implementation, which implements this SRFI in the (srfi 139) library.

Syntax parameters are also implemented by, at least, Chibi-Scheme, Guile, and Racket.

Acknowledgements

This SRFI wouldn't probably exist if Eli Barzilay, Ryan Culpepper, and Matthew hadn't published their paper Keeping it Clean with Syntax Parameters, from which bits were shamelessly reused for this SRFI. Much of this specification is likewise derived from the Guile Reference Manual and the Racket documentation.

Copyright

© 2016 Marc Nieper-Wißkirchen. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


Editor: Arthur A. Gleckler