On Saturday, 18 June 2011 at 14:11, Benjohn Barnes wrote:
I think I must not be understanding something basic about macro expansion.
I've reduced a more complex failure with macros that I am having to this simple example:
; Define a function. (define (inc x) (+ 1 x))
;Define a macro using the function. (define-macro (macro-inc x) (inc x))
; This works okay. (define two (inc 1))
; This fails on load with: ; *** ERROR IN #<procedure #2>, "macro-test.scm"@5.30 -- Unbound variable: inc (define macro-two (macro-inc 1))
As others have pointed out, this is works in the REPL, because each expression gets macroexpanded and evaluated before the next expression is macroexpanded. A different way to explain why this happens is to observe the fact that (load) and (include) works as if you'd have written
(begin (define (inc x) (+ 1 x)) (define-macro (macro-inc x) (inc x)) (define two (inc 1)) (define macro-two (macro-inc 1)))
, which gives the "Unbound variable: inc" error even in the REPL, because the entire begin form is macro expanded before any of it is evaluated.
The interaction between run-time and compile time, especially in an environment with a REPL, is quite complicated. The solution Marc linked to is the preferred way to do it in vanilla Gambit.
The way to do this in Black Hole is to separate the code that is needed at compile time into a separate module. Here's a Black Hole version of your example:
t[master]$ cat > inc.scm (define (inc x) (+ 1 x))
t[master]$ cat > example.scm (syntax-begin (import inc)) (import inc) (define-macro (macro-inc x) (inc x)) (define two (inc 1)) (define macro-two (macro-inc 1))
t[master]$ bsc Loaded Black Hole. Gambit v4.6.1
(import example) two
2
macro-two
2
The important part of this is (syntax-begin (import inc)). It imports inc into the macro namespace. Without it, macro-inc wouldn't have worked.
Because inc is also used at runtime, it has to be imported as usual as well.
A caveat: syntax-begin has turned out to be really tricky to implement right, and I might remove it in favor of a more intelligent import form that automatically infers which phases it has to import to. I don't recommend putting anything but import forms in it.
/Per