tags:

views:

36

answers:

1

Hello,

Consider this unhygienic Scheme macro:

(define-macro for
  (lambda (i i1 i2 . body)
    (let ((start (gensym))
          (stop  (gensym))
          (loop  (gensym)))
      `(let ((,start ,i1)
             (,stop  ,i2))
         (let ,loop ((,i ,start))
              (if (< ,i ,stop)
                  (begin ,@body
                         (,loop (+ 1 ,i)))))))))

It implements a for loop (I'm working with Gauche and Gambit):

> (for i 1 5
       (print i))
1
2
3
4
#

However, since I did not rename if, this will certainly break:

(let ((if 'x))
  (for i 1 5
       (print i)))

It starts counting and never stops.

Now, I have tried macroexpanding this and couldn't see exactly why it loops instead of just signaling an error.

The expansion in Gambit (without the if redefinition part) is this:

(let ((#:start15 1)
      (#:stop16 5))
 ((letrec ((#:loop17 (lambda (i) 
                       (if (< i #:stop16) 
                           (begin (print i)
                                  (#:loop17 (+ 1 i))))))) 
     #:loop17)
   #:start15))))

And in Gauche:

(let ((:start4 1)
      (:stop5 5))
  (let :loop6 ((i :start4))
       (if (< i :stop5)
           (begin (print i)
                  (:loop6 (+ 1 i))))))

but if I'm inserting the symbol x where the if was, I'm using a symbol where a special form or procedure is expected... Why didn't the interpreter just stop and complain about that?

+5  A: 

You're binding if to be a plain value, which means that (if blah blah blah) is no longer a special form -- and that means that it's turning to a function application. This application would obviously result in an error, but since Scheme is strict, you need to evaluate the arguments to this function before you try to apply it. (It could stop with an error as soon as the function expression evaluates to a non-function, but implementations rarely do that.) So the recursive loop call is evaluated first -- but that leads to another if function call, etc.

But more importantly:

    define-macro is bad for your health

Eli Barzilay
Ah, thank you! I missed the evaluation order issue. As to `define-macro` and my health, I'm actually trying to dig up `define-macro` problems to include in course notes. :-)
Jay
In that case keep using it!
Eli Barzilay