schemeracketr6rs

Racket R6RS support: syntax-case


This simple R6RS program:

#!r6rs
(import (rnrs base)
        (rnrs syntax-case)
        (rnrs io simple))

(define-syntax stest
  (lambda (x)
    (syntax-case x ()
      ((_ z) #'(z 0)))))

(stest display)

works with Chez, Guile and Ypsilon but not Racket. It gives me this:

test.scm:7:3: lambda: unbound identifier in the transformer environment;
also, no #%app syntax transformer is bound

My question is, is it broken for R6RS or do I have to do something else? I'm testing with version 6.12.


Solution

  • The Racket implementation of R6RS is not non-compliant in this case. Indeed, if anything, it is obeying the standard more closely: your program as-written is not careful about import phases. The problem is that define-syntax evaluates its right-hand side during expand-time, as noted by section 11.2.2 Syntax definitions:

    Binds <keyword> to the value of <expression>, which must evaluate, at macro-expansion time, to a transformer.

    Unlike other Scheme standards, R6RS takes care to distinguish between phases, since it permits arbitrary programming at compile-time (while other Scheme standards do not). Therefore, section 7.1 Library form specifies how to import libraries at specific phases:

    Each <import spec> specifies a set of bindings to be imported into the library, the levels at which they are to be available, and the local names by which they are to be known. An <import spec> must be one of the following:

    <import set>
    (for <import set> <import level> ...)
    

    An <import level> is one of the following:

    run
    expand
    (meta <level>)
    

    where <level> represents an exact integer object.

    Therefore, you need to import (rnrs base) at both the run and expand phases, and you need to import (rnrs syntax-case) at the expand phase. You can do this with the following program:

    #!r6rs
    (import (for (rnrs base) run expand)
            (for (rnrs syntax-case) expand)
            (rnrs io simple))
    
    (define-syntax stest
      (lambda (x)
        (syntax-case x ()
          ((_ z) #'(z 0)))))
    
    (stest display)
    

    This program works in Racket. I have not tested if it also works on the other Scheme implementations you listed, but it ought to if they are standards-compliant.