Macro calling macro gives "undefined variable" in Gambit Scheme

308 views Asked by At

In Gambit Scheme, I can't seem to invoke a macro in the definition of another macro if I compile the file. Here is a contrived example:

;;;; example.scm

(define-macro (w/gensyms gs body)
  `(let ,(map (lambda (g) `(,g (gensym ',g)))
              gs)
     ,body))

(define-macro (compose-macro f g)
  (w/gensyms (x)
    `(lambda (,x) (,f (,g ,x)))))

(define my-cadr
  (lambda (x)
    ((compose-macro car cdr) x)))

;; $ gsc example.scm
;; *** ERROR IN #<procedure #2> -- Unbound variable: w/gensyms

However, if I load the file with the (include ...) special form in the interpreter, it works

$ gsi
> (include "example.scm")
> (pp my-cadr)
(lambda (x) ((lambda (#:x0) (car (cdr #:x0))) x))

Does anyone know what is going on here? Can I convince Gambit to let me use w/gensyms in the definition of another macro in a compiled file?

2

There are 2 answers

2
soegaard On

This is most likely related to phases.

Try this:

Put w/gensyms in a file a.scm and put compose-macro in a file b.scm that imports a.scm.

0
feeley On

This is a phasing problem. You want the definition of w/gensyms to be available in the body of subsequent macros. This can be achieved with a for-syntax macro that forces the evaluation of the macro definition at syntax expansion time:

(define-macro (for-syntax . body)
  (eval `(begin ,@body))
  `(begin))

(for-syntax
 (define-macro (w/gensyms gs body)
   `(let ,(map (lambda (g) `(,g (gensym ',g)))
               gs)
      ,body)))

If you want the macro to be available both from within other macro definitions and within non-macro definition code you can use this instead:

(define-macro (for-syntax . body)
  (eval `(begin ,@body))
  `(begin ,@body))

For this specific example, since you are using the macro at a single place, you could have done this:

(define-macro (compose-macro f g)

   (define-macro (w/gensyms gs body)
     `(let ,(map (lambda (g) `(,g (gensym ',g)))
                 gs)
        ,body))

  (w/gensyms (x)
    `(lambda (,x) (,f (,g ,x)))))

A related approach to address phasing issues is to put the definition of w/gensyms and other macros in the file "macros.scm" and do:

(define-macro (compose-macro f g)

  (include "macros.scm")

  (w/gensyms (x)
    `(lambda (,x) (,f (,g ,x)))))