[gambit-list] eval order, define vs define-macro

Marc Feeley feeley at iro.umontreal.ca
Thu May 11 11:17:26 EDT 2006

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))


More information about the Gambit-list mailing list