Next: , Previous: , Up: stdlib syntax-case   [Index]


5.12.9 Derived forms and procedures

The forms and procedures described in this section can be defined in terms of the forms and procedures described in earlier sections of this chapter.

Syntax: with-syntax ((?pattern ?expression) …) ?body

The with-syntax form is used to bind pattern variables, just as let is used to bind variables. This allows a transformer to construct its output in separate pieces, then put the pieces together.

Each ?pattern is identical in form to a syntax-case pattern. The value of each ?expression is computed and destructured according to the corresponding ?pattern, and pattern variables within the ?pattern are bound as with syntax-case to the corresponding portions of the value within ?body.

The with-syntax form may be defined in terms of syntax-case as follows.

(define-syntax with-syntax
  (lambda (x)
    (syntax-case x ()
      ((_ ((p e0) ...) e1 e2 ...)
       (syntax (syntax-case (list e0 ...) ()
                 ((p ...) (let () e1 e2 ...))))))))

As example of deconstructing a syntax object:

(define stx
  (datum->syntax #'display '(1 2 3)))

(with-syntax (((a b ...) stx))
  (syntax->datum #'a))
⇒ 1

The following definition of cond demonstrates the use of with-syntax to support transformers that employ recursion internally to construct their output. It handles all cond clause variations and takes care to produce one-armed if expressions where appropriate.

(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

The quasisyntax form is similar to syntax, but it allows parts of the quoted text to be evaluated, in a manner similar to the operation of quasiquote (see baselib quasiquotation).

Within a quasisyntax template, subforms of unsyntax and unsyntax-splicing forms are evaluated, and everything else is treated as ordinary template material, as with syntax.

The value of each unsyntax subform is inserted into the output in place of the unsyntax form, while the value of each unsyntax-splicing subform is spliced into the surrounding list or vector structure. Uses of unsyntax and unsyntax-splicing are valid only within quasisyntax expressions.

A quasisyntax expression may be nested, with each quasisyntax introducing a new level of syntax quotation and each unsyntax or unsyntax-splicing taking away a level of quotation. An expression nested within n quasisyntax expressions must be within n unsyntax or unsyntax-splicing expressions to be evaluated.

As noted in scheme lex datum abbreviations:

#`?template   ≡ (quasisyntax ?template)
#,?template   ≡ (unsyntax ?template)
#,@?template  ≡ (unsyntax-splicing ?template)

The quasisyntax keyword can be used in place of with-syntax in many cases. For example, the definition of case shown under the description of with-syntax above can be rewritten using quasisyntax as follows.

(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 ...)
                      #'(when (memv t '(k ...))
                          (begin e1 e2 ...))))
                   (syntax-case c1 ()
                     (((k ...) e1 e2 ...)
                      #`(if (memv t '(k ...))
                            (begin e1 e2 ...)
                          #,(f (car cmore)
                               (cdr cmore))))))))))))

Uses of unsyntax and unsyntax-splicing with zero or more than one subform are valid only in splicing (list or vector) contexts. (unsyntax template …) is equivalent to (unsyntax template) ..., and (unsyntax-splicing template ...) is equivalent to (unsyntax-splicing template) .... These forms are primarily useful as intermediate forms in the output of the quasisyntax expander.

NOTE Uses of unsyntax and unsyntax-splicing with zero or more than one subform enable certain idioms, such as ‘#,@#,@’, which has the effect of a doubly indirect splicing when used within a doubly nested and doubly evaluated quasisyntax expression, as with the nested quasiquote examples shown in section baselib quasiquotation.


Next: , Previous: , Up: stdlib syntax-case   [Index]