Chicken » Macros

## Macros

CHICKEN supports standard R5RS `syntax-rules` macros and a low-level macro system based on explicit renaming.

### Macro definitions

#### define-syntax

`(define-syntax IDENTIFIER TRANSFORMER)` syntax

  Defines a macro named `IDENTIFIER` that will transform an expression with `IDENTIFIER` in operator position according to `TRANSFORMER`. The transformer expression must the result of a call to`er-macro-transformer` or `ir-macro-transformer`, or it must be a `syntax-rules` form. If `syntax-rules` is used, the usual R5RS semantics apply. If `TRANSFORMER` is a transformer, then its transformer procedure will be called on expansion with the complete s-expression of the macro invocation, a rename procedure that hygienically renames identifiers and a comparison procedure that compares (possibly renamed) identifiers (see the section "Explicit renaming macros" below for a detailed explanation on non-R5RS macros).

  `define-syntax` may be used to define local macros that are visible throughout the rest of the body in which the definition occurred, i.e.

       (let ()
         ...
         (define-syntax foo ...)
         (define-syntax bar ...)
         ...)

  is expanded into

       (let ()
         ...
         (letrec-syntax ((foo ...) (bar ...))
           ...) )

  `syntax-rules` supports SRFI-46 in allowing the ellipsis identifier to be user-defined by passing it as the first argument to the `syntax-rules` form. Also, "tail" patterns of the form

       (syntax-rules ()
         ((_ (a b ... c)
           ...

  are supported.

  The effect of destructively modifying the s-expression passed to a transformer procedure is undefined.

#### er-macro-transformer

`(er-macro-transformer PROCEDURE)` procedure

  Returns an explicit-remnaming transformer object wrapping the syntax-transformer procedure `PROCEDURE`. The procedure will be called with the form to be expanded and rename and compare procedures and perform explicit renaming to maintain hygiene. See below for more information about explicit renaming macros.

#### ir-macro-transformer

`(ir-macro-transformer PROCEDURE)` procedure

  Returns a implicit-renaming transformer object wrapping the syntax-transformer procedure `PROCEDURE`. The procedure will be called with the form to be expanded and an inject and compare procedure and perform implicit renaming to maintain hygiene. See below for more information about implicit renaming macros.

#### strip-syntax

`(strip-syntax EXPRESSION)` procedure

  Strips all syntactical information from `EXPRESSION`, returning a new expression where symbols have all context-information removed.

### Explicit renaming macros

The low-level macro facility that CHICKEN provides is called "explicit renaming" and allows writing hygienic or non-hygienic macros procedurally. When given a the return value of the one of the procedures `er-macro-transformer` or `ir-macro-transformer` instead of a `syntax-rules` form, `define-syntax` evaluates the procedure in a distinct expansion environment (initially having access to the exported identifiers of the `scheme` module). The procedure takes an expression and two other arguments and returns a transformed expression.

For example, the transformation procedure for a `call` macro such that `(call proc arg ...)` expands into `(proc arg ...)` can be written as

     (er-macro-transformer
       (lambda (exp rename compare)
         (cdr exp)))

Expressions are represented as lists in the traditional manner, except that identifiers are represented as special uninterned symbols.

The second argument to a transformation procedure is a renaming procedure that takes the representation of an identifier as its argument and returns the representation of a fresh identifier that occurs nowhere else in the program. For example, the transformation procedure for a simplified version of the `let` macro might be written as

     (er-macro-transformer
       (lambda (exp rename compare)
         (let ((vars (map car (cadr exp)))
               (inits (map cadr (cadr exp)))
               (body (cddr exp)))
           `((lambda ,vars ,@body)
             ,@inits))))

This would not be hygienic, however. A hygienic `let` macro must rename the identifier `lambda` to protect it from being captured by a local binding. The renaming effectively creates a fresh alias for `lambda`, one that cannot be captured by any subsequent binding:

     (er-macro-transformer
       (lambda (exp rename compare)
         (let ((vars (map car (cadr exp)))
               (inits (map cadr (cadr exp)))
               (body (cddr exp)))
           `((,(rename 'lambda) ,vars ,@body)
             ,@inits))))

The expression returned by the transformation procedure will be expanded in the syntactic environment obtained from the syntactic environment of the macro application by binding any fresh identifiers generated by the renaming procedure to the denotations of the original identifiers in the syntactic environment in which the macro was defined. This means that a renamed identifier will denote the same thing as the original identifier unless the transformation procedure that renamed the identifier placed an occurrence of it in a binding position.

Identifiers obtained from any two calls to the renaming procedure with the same argument will necessarily be the same, but will denote the same syntactical binding. It is an error if the renaming procedure is called after the transformation procedure has returned.

The third argument to a transformation procedure is a comparison predicate that takes the representations of two identifiers as its arguments and returns true if and only if they denote the same thing in the syntactic environment that will be used to expand the transformed macro application. For example, the transformation procedure for a simplified version of the `cond` macro can be written as

     (er-macro-transformer
       (lambda (exp rename compare)
         (let ((clauses (cdr exp)))
    (if (null? clauses)
        `(,(rename 'quote) unspecified)
        (let* ((first (car clauses))
    	   (rest (cdr clauses))
    	   (test (car first)))
          (cond ((and (symbol? test)
    		  (compare test (rename 'else)))
    	     `(,(rename 'begin) ,@(cdr first)))
    	    (else `(,(rename 'if)
    		    ,test
    		     (,(rename 'begin) ,@(cdr first))
    		     (,(r 'cond) ,@rest)))))))))

In this example the identifier `else` is renamed before being passed to the comparison predicate, so the comparison will be true if and only if the test expression is an identifier that denotes the same thing in the syntactic environment of the expression being transformed as `else` denotes in the syntactic environment in which the `cond` macro was defined. If `else` were not renamed before being passed to the comparison predicate, then it would match a local variable that happened to be named `else`, and the macro would not be hygienic. The final recursive call to `cond` also needs to be renamed because someone might create an alias for this macro and use it in a `let` where `cond` is an ordinary variable.

Some macros are non-hygienic by design. For example, the following defines a `loop` macro that implicitly binds `exit` to an escape procedure. The binding of `exit` is intended to capture free references to `exit` in the body of the loop, so `exit` is not renamed.

     (define-syntax loop
       (er-macro-transformer
         (lambda (x r c)
           (let ((body (cdr x)))
             `(,(r 'call-with-current-continuation)
               (,(r 'lambda) (exit)
                (,(r 'let) ,(r 'f) () ,@body (,(r 'f)))))))))

Suppose a `while` macro is implemented using `loop`, with the intent that `exit` may be used to escape from the `while` loop. The `while` macro cannot be written as

     (define-syntax while
       (syntax-rules ()
         ((while test body ...)
          (loop (if (not test) (exit #f))
                body ...))))

because the reference to `exit` that is inserted by the `while` macro is intended to be captured by the binding of `exit` that will be inserted by the `loop` macro. In other words, this `while` macro is not hygienic. Like `loop`, it must be written using procedurally:

     (define-syntax while
       (er-macro-transformer
         (lambda (x r c)
           (let ((test (cadr x))
                 (body (cddr x)))
             `(,(r 'loop)
               (,(r 'if) (,(r 'not) ,test) (exit #f))
               ,@body)))))

Think about it: If we did rename `exit`, it would refer to an `exit` procedure existing in the context of the macro's definition. That one actually exists; it is the procedure that exits the Scheme interpreter. Definitely not the one we want :) So now we make it refer to an `exit` that's locally bound in the environment where the macro is expanded.

Note: this implementation of explicit-renaming macros allows passing arbitrary expressions to the renaming and comparison procedures. When being renamed, a fresh copy of the expression will be produced, with all identifiers renamed appropriately. Comparison also supports arbitrary expressions as arguments.

### Implicit renaming macros

Explicit renaming macros generally require the user to perform quite a few renames, because most identifiers that aren't taken from the input expression should generally be inserted hygienically. It would make more sense to give the output expression as-is, and only explicitly convert those identifiers that you want to treat as unhygienic.

This can be done with implicit renaming macros. They just swap the default insertion "mode" from unhygienic to hygienic, so to speak. Here's the `cond` example from the previous section as an ir-macro:

     (ir-macro-transformer
       (lambda (exp inject compare)
         (let ((clauses (cdr exp)))
           (if (null? clauses)
               `(quote unspecified)
               (let* ((first (car clauses))
                      (rest (cdr clauses))
                      (test (car first)))
                 (cond ((and (symbol? test)
                             (compare test 'else))
                        `(begin ,@(cdr first)))
                       (else `(if ,test
                                  (begin ,@(cdr first))
                                  (cond ,@rest)))))))))

In this example the identifier `else` does not need to be renamed before being passed to the comparison predicate because it is already implicitly renamed. This comparison will also be true if and only if the test expression is an identifier that denotes the same thing in the syntactic environment of the expression being transformed as `else` denotes in the syntactic environment in which the `cond` macro was defined. If `else` were not renamed before being passed to the comparison predicate, then it would match a local variable that happened to be named `else`, and the macro would not be hygienic.

As you can see, the code is a lot clearer because it isn't obscured by excessive renaming.

Here's the `loop` macro so you can see how hygiene can be broken with implicit renaming macros:

     (define-syntax loop
       (ir-macro-transformer
         (lambda (expr inject compare)
           (let ((body (cdr expr)))
             `(call-with-current-continuation
               (lambda (,(inject 'exit))
                (let f () ,@body (f))))))))

The `while` macro is a little trickier: do we inject the call to `exit` or not? Just like the explicit renaming macro version did not rename it, we must inject it to allow it to be captured by the `loop` macro:

     (define-syntax while
       (ir-macro-transformer
         (lambda (expr inject compare)
           (let ((test (cadr expr))
                 (body (cddr expr)))
             `(loop
               (if (not ,test) (,(inject 'exit) #f))
               ,@body)))))

Note: Just like explicit renaming macros, this implementation of implicit renaming macros allow passing arbitrary expressions to the injection and comparison procedures. The injection procedure also return fresh copies of its input.

Previous: Non-standard macros and special forms

Next: Modules