Search code examples
macrosschememit-scheme

Difference between an implementation of 'when' as a function vs. as a macro


What exactly is different between these implementations of 'when'?

(define-syntax when
  (syntax-rules ()
    ((_ pred b1 ...)
     (if pred (begin b1 ...)))))

vs.

(define (my-when pred b1 ...)
  (if pred (begin b1 ...)))

For example, when 'my-when' is used in this for loop macro:

(define-syntax for
  (syntax-rules ()
    ((_ (i from to) b1 ...)
     (let loop((i from))
       (my-when (< i to)
                  b1 ...
                  (loop (+ i 1)))))))

an error occurs:

(for (i 0 10) (display i))

; Aborting!: maximum recursion depth exceeded

I do not think 'when' can be implemented as a function, but I do not know why...


Solution

  • Scheme has strict semantics.

    This means that all of a function's parameters are evaluated before the function is applied to them.

    Macros take source code and produce source code - they don't evaluate any of their parameters.

    (Or, well, I suppose they do, but their parameters are syntax - language elements - rather than what you normally think of as values, such as numbers or strings. Macro programming is meta-programming. It's important to be aware of which level you're programming at.)

    In your example this means that when my-when is a function, (loop (+ i 1)) must be evaluated before my-when can be applied to it.
    This leads to an infinite recursion.

    When it's a macro, the my-when form is first replaced with the equivalent if-form

     (if (< i to)
         (begin
             b1 ...
             (loop (+ i 1))))
    

    and then the whole thing is evaluated, which means that (loop (+ i 1)) only gets evaluated when the condition is true.