Hygenic Macros

Scheme, like virtually all LISP dialects, has a built-in macro facility. However, the Scheme 'hygenic macro' system is quite different from the traditional (defmacro) approach used in other dialects, and is one of the more controversial aspects of the language. They are, in essence, a mechanism for defining new syntactic structures

A Scheme macro is defined using two forms, (let-syntax) and (syntax-rules). (let-syntax) is the simpler of thes two: it takes an identifier and the list of rules returned by (syntax-rules). (syntax-rules), in turn, takes a list of 'syntax' identifiers to be used by the macro, and a series of list pairs. Each of these pairs consists of a rule, which acts as a template or pattern for the macro, and a result, which is the code actually emitted. For example, this is a simple macro implementing a (while) loop:

   (define-syntax while 
     ; a simple while loop
     (syntax-rules ()
       ((while condition expr1 expr2 ...)
  

This can be thus used:

   > (define x 1)
   > x
   1
   > (while (< x 5)
            (display x)
            (display " ")
            (set! x (+ 1 x)))
   1 2 3 4
  

The (syntax-rules) mechanism is very powerful; for example, here is a detailed implementation of a Pascal-like FOR loop syntax:

 
   (define-syntax for
     ; a Pascal-style for loop
     (syntax-rules (:= to downto step)
       ; rule for complete upwards for loop
       ((for index := start to finish step modifier expr1 expr2 ...)
        (begin
          (define index start)
          (let loop ()
            (if (<= index finish)
                (begin expr1 expr2 ... 
                      (set! index (+ index modifier)) 
                      (loop))
                ; when finished, adjust index to correct final value 
                (set! index (- index modifier)))))) 
    
       ; rules for complete downwards for loop 
       ((for index := start downto finish step modifier expr1 expr2 ...)
        (begin 
          (define index start)
          (let loop ()
            (if (>= index finish)
                (begin expr1 expr2 ... 
                       (set! index (- index modifier)) 
                       (loop))
                ; when finished, adjust index to correct final value 
                (set! index (+ index modifier))))))
    
       ; rule for upward loop with default step
       ((for index := start to finish expr1 expr2 ...)
        (for index := start to finish step 1 expr1 expr2 ...))
    
       ; rule for downward loop with default step
       ((for index := start downto finish expr1 expr2 ...)
        (for index := start downto finish step 1 expr1 expr2 ...))))
  

Which can then be used as so:

   > (for x := 2 to 12 step 2
          (display x)
          (display " "))

   2 4 6 8 10 12
   > x
   12
  

An instance of a hygenic macro has its own local variable space, like lambda functions do; as a result, hygenic macros avoid the problem of variable capture. However, critics point out that variable capture, while often a problem, can also be a useful tool, and claim that forcibly preventing it makes certain common idioms unfeasible. The complexity of the mechanism has also been heavily criticized: the terms 'fascist' and 'baroque' have both been applied to Scheme macro system. Nontheless, they are a vitally important part of the language; most of the basic facilities of the language are usually implemented as macros, including (if), (cond), (define), (and), (or) , (let), and (do).

Contents Previous Next