Node: Macros, Previous: Quasiquotation, Up: Expressions



Macros

STKLOS supports hygienic macros such as the ones defined in R5RS

as well as low level macros. Low level macros are defined with define-macro whereas R5RS macros are defined with define-syntax 1. Hygienic macros use the implementation of "Macro by Example" (Eugene Kohlbecker, R4RS ) by Dorai Sitaram. This implementation generates low level STKLOS macros. This implementation of hygienic macros is not expensive.

Its major drawback is that these macros are not referentially transparent (see section `Macros' in R4RS ). Lexically scoped macros (i.e., let-syntax and letrec-syntax) are not supported. In any case, the problem of referential transparency gains poignancy only when let-syntax and letrec-syntax are used. So you will not be courting large-scale disaster unless you're using system-function names as local variables with unintuitive bindings that the macro can't use. However, if you must have the full R5RS macro functionality, you can load do

     (require "full-syntax")
     
to have access to the more featureful (but also more expensive) versions of syntax-rules. Requiring full-syntax load the version 2.1 of an implementation of hygienic macros by Robert Hieb and R. Kent Dybvig.

TODO: DEFINE THE LOW LEVEL EXPANDER MECHANISM

define-macro [<name> <formals] <body> STKLOS Syntax
define-macro <name> [lambda <formals> <body>] STKLOS Syntax
define-macro can be used to define low-level macro (i.e. non hygienic macros. This for is similar to the defmacro of Common Lisp.
          (define-macro (incr x) `(set! ,x (+ ,x 1)))
          (let ((a 1)) (incr a) a)   => 2
          
          (define-macro (when test . body)
            `(if ,test ,@(if (null? (cdr body)) body `((begin ,@body)))))
          (macro-expand '(when a b)) => (if a b)
          (macro-expand '(when a b c d))
                                     => (if a (begin b c d))
          
          (define-macro (my-and . exprs)
            (cond
             ((null? exprs)        #t)
             ((= (length exprs) 1) (car exprs))
             (else                 `(if ,(car exprs)
          			      (my-and ,@(cdr exprs))
          			      #f))))
          (macro-expand '(my-and a b c))
                                    => (if a (my-and b c) #f)
          

define-syntax <identifier> <transformer-spec> R5RS
<Define-syntax> extends the top-level syntactic environment by binding the <identifier> to the specified transformer.

Note: <transformer-spec> should be an instance of syntax-rules.

          (define-syntax let*
            (syntax-rules ()
              ((let* () body1 body2 ...)
               (let () body1 body2 ...))
              ((let* ((name1 val1) (name2 val2) ...)
                 body1 body2 ...)
               (let ((name1 val1))
                 (let* (( name2 val2) ...)
                   body1 body2 ...))))
          

syntax-rules <literals> <syntax-rule> ... R5RS
<literals> is a list of identifiers, and each <syntax-rule> should be of the form
          (pattern template)
          

An instance of <syntax-rules> produces a new macro transformer by specifying a sequence of hygienic rewrite rules. A use of a macro whose name is associated with a transformer specified by <syntax-rules> is matched against the patterns contained in the <syntax-rules>, beginning with the leftmost syntax-rule. When a match is found, the macro use is transcribed hygienically according to the template.

Each pattern begins with the name for the macro. This name is not involved in the matching and is not considered a pattern variable or literal identifier.

Note: For a complete description of the Scheme pattern language, refer to R5RS .

let-syntax <bindings> <body> R5RS
<Bindings> should have the form
          ((<keyword> <transformer spec>) ...)
          

Each <keyword> is an identifier, each <transformer spec> is an instance of syntax-rules, and <body> should be a sequence of one or more expressions. It is an error for a <keyword> to appear more than once in the list of keywords being bound.

The <body> is expanded in the syntactic environment obtained by extending the syntactic environment of the let-syntax expression with macros whose keywords are the <keyword>s, bound to the specified transformers. Each binding of a <keyword> has <body> as its region.

Note: let-syntax is available only after having required the file "full-syntax".

          (let-syntax ((when (syntax-rules ()
          		     ((when test stmt1 stmt2 ...)
          		      (if test
          			  (begin stmt1
          				 stmt2 ...))))))
            (let ((if #t))
              (when if (set! if 'now))
              if))                           =>  now
          
          (let ((x 'outer))
            (let-syntax ((m (syntax-rules () ((m) x))))
              (let ((x 'inner))
                (m))))                       =>  outer
          

letrec-syntax <bindings> <body> R5RS
Syntax of letrec-syntax is the same as for let-syntax.

The <body> is expanded in the syntactic environment obtained by extending the syntactic environment of the letrec-syntax expression with macros whose keywords are the <keyword>s, bound to the specified transformers. Each binding of a <keyword> has the <bindings> as well as the <body> within its region, so the transformers can transcribe expressions into uses of the macros introduced by the letrec-syntax expression. Note: letrec-syntax is available only after having required the file "full-syntax".

          (letrec-syntax
            ((my-or (syntax-rules ()
                      ((my-or) #f)
                      ((my-or e) e)
                      ((my-or e1 e2 ...)
                       (let ((temp e1))
                         (if temp
                             temp
                             (my-or e2 ...)))))))
            (let ((x #f)
                  (y 7)
                  (temp 8)
                  (let odd?)
                  (if even?))
              (my-or x
                     (let temp)
                     (if y)
                     y)))        =>  7
          

macro-expand form STKLOS Procedure
Returns the macro expansion of form if it is a macro call, otherwise form is returned unchanged.
          (define-macro (incr x) `(set! ,x (+ ,x 1)))
          (macro-expand '(incr foo)) => (set! foo (+ foo 1))
          (macro-expand '(car bar))  => (car bar)
          


Footnotes

  1. documentation about hygienic macros has been stolen in the SLIB manual