macrosschemereserved-wordsmit-schemesyntax-rules

Why can't a Scheme macro with the name "if" be defined?


Here is a very simple Scheme macro that works, running on MIT/GNU Scheme 12.1:

1 ]=> (define-syntax example
        (syntax-rules () 
          ((_) 'ok)))

;Value: example

1 ]=> (example)

;Value: ok

However, defining the same macro with the name if gives an error:

1 ]=> (define-syntax if
        (syntax-rules () 
          ((_) 'ok)))

;Premature reference to reserved name: if
;To continue, call RESTART with an option number:
; (RESTART 1) => Return to read-eval-print level 1.

I've never used Scheme macros before, but I was under the impression that Scheme doesn't have "reserved" names and that any name in Scheme could be redefined. In fact, redefining if to a normal procedure works as expected:

1 ]=> (define (if) 'ok)

;Value: if

1 ]=> (if)

;Value: ok

I skimmed through the section on macros in the reference manual (https://www.gnu.org/software/mit-scheme/documentation/stable/mit-scheme-ref/Macros.html), but I couldn't find the cause of the problem.

Is there something about the internal operation of syntax-rules that is causing this to fail?

UPDATE 1

Locally redefining if using let-syntax works:

1 ]=> (let-syntax ((if (syntax-rules () ((_) 'ok))))
        (if))

;Value: ok

I'm still not sure why define-syntax doesn't work.

UPDATE 2

It is possible to shadow other built-in keywords with macros:

1 ]=> (define-syntax define (syntax-rules () ((_) 'ok)))

;Value: define

1 ]=> (define)

;Value: ok

1 ]=> (define-syntax set! (syntax-rules () ((_) 'ok)))

;Value: set!

1 ]=> (set!)

;Value: ok

1 ]=> (define-syntax lambda (syntax-rules () ((_) 'ok)))

;Value: lambda

1 ]=> (lambda)

;Value: ok

1 ]=> (define-syntax cond (syntax-rules () ((_) 'ok)))

;Value: cond

1 ]=> (cond)

;Value: ok

Hilariously, you can even redefine define-syntax:

1 ]=> (define-syntax define-syntax (syntax-rules () ((_) 'ok)))

;Value: define-syntax

1 ]=> (define-syntax)

;Value: ok

However, attempting to redefine the keyword syntax-rules gets a similar error as for redefining if (note: I started a new REPL because the last macro shadowed define-syntax):

1 ]=> (define-syntax syntax-rules (syntax-rules () ((_) 'ok)))

;Premature reference to reserved name: syntax-rules
;To continue, call RESTART with an option number:
; (RESTART 1) => Return to read-eval-print level 1.

Solution

  • if can be defined as a macro at the toplevel in many Scheme implementations. I tested

     (define-syntax if
            (syntax-rules () 
              ((_) 'ok)))
    (if) ; returns 'ok
    

    on Guile 3.0.9, Chicken 5.3.0, Gauche 0.9.9, Chibi 0.10.0 (in the form of the latest from git), Chez 9.6.2, Gambit 4.9.5, Kawa 3.1.1, and MIT/GNU Scheme 11.2. All accepted and ran the code without errors. You said you're using MIT/GNU Scheme 12.1, so something that changed between the two versions is responsible.

    Interestingly, the redefinition of syntax-rules gives the error in 11.2. Guile, Chicken, Gauche, Chibi, and Gambit all accepted it, while Chez and Kawa threw errors.