On 11-May-06, at 10:38 AM, Stephane Le Cornec wrote:
(define (foo x) x) (define-macro (bar x) `(+ ,x ,(foo x))) (define (baz x) (bar x))
(load "test")
*** ERROR IN #<procedure #2>, "test.scm"@2.1 -- Unbound variable: foo 1>
I was a little surprised of the result. OTOH:
(define (baz2 x) (bar2 x)) (define-macro (bar2 x) `(+ ,x 2))
fails on (baz2 2) as expected while the reverse works properly.
I don't understand why the eval order does not work for define- macro. Is the behavior an implementation choice? And if so why is it wanted?
Assume the following code is in the file "test.scm":
(define (foo x) x) (define-macro (bar x) `(+ ,x ,(foo x))) (define (baz x) (bar x))
and you compile this code with
gsc -dynamic test.scm
and then run the code with
gsi test.o1
Ask yourself when the variables foo and baz will be set.
The answer is at run time, that is when gsi loads the file test.o1 . On the other hand the macro bar must be known at compile time (also known as expansion time), that is when gsc compiles the file test.scm, because the definition of baz depends on the expansion that results from calling the macro bar. But in your code, the definition for bar depends on foo which is only known at run time. That's why foo is not defined when you call bar in baz.
For consistency with the compiler, the interpreter has the same semantics. It also has an expansion time and a run time, but these are two separate phases (i.e. expansion of the source code is completely done, then the interpreter runs the code).
Because it can't really do otherwise, the REPL evaluates each form individually (i.e. expansion and then run, for each expression entered). So if you type those 3 definitions at the REPL it will work, because when you enter the definition for baz, the function foo has been defined.
Does that clarify things?
The reason why this is counterintuitive is that macros give the illusion that function definitions and macro definitions are evaluated in the same "world". Although the same language is used, there are really two worlds: the run time world and the expansion time world. This makes it hard to write macros that need to share some expansion time function or state. Here's one way to achieve this.
(define-macro (at-expand-time expr) (eval expr) `(begin))
(at-expand-time (define (foo x) x)) (define-macro (bar x) `(+ ,x ,(foo x))) (define (baz x) (bar x))
Marc