[plt-scheme] Usage of raise-syntax-error

From: Jens Axel Soegaard (jensaxel at soegaard.net)
Date: Sun Oct 21 07:49:58 EDT 2007

Grant Rettke wrote:

 > The following example shows one reason to use syntax-case instead of
 > syntax-rules: custom error reporting.
 >
 > Is it worth cutting over from syntax-rules to syntax-case just to have
 > access to that?

Yes!

I have advocating that view for years.

 > What does it provide?

It enables the macro writer to give error messages worded in terms
of the original syntax. Suppose you write a macro foo, which expands
into something that uses bar. The error presented to the user
will be worded in terms of bar - which is very confusing for an
user that didn't write foo (how is he supposed to know that foo
expands into bar?). Worse when he searches for "bar" in his source
code, he can't find it. Even worse: Suppose bar is again a macro that
expands into ... The user will get an error in terms of something
very low level.


A concrete simple example. Consider the macro (my-let-1 (v ...) b)
which binds variables v ... to the number 1 in the body b.
An usage example (my-let-1 (a b) (+ a b) evaluates to 2.

With syntax-rules this is easy to implement:

   (define-syntax my-let-1
     (syntax-rules ()
       ((my-let-1 (v ...) b)
        (let ((v 1) ...) b))))

   > (my-let-1 (a b) (+ a b))
   2

Now what happens if we use it incorrectly?

   > (my-let-1 (a a) (+ a b))
   let: duplicate identifier in: a


The error is not worded in terms of the original syntax.

To fix it, we first make it a syntax-rules macro.

   (define-syntax (my-let-1 stx)
     (syntax-case  stx ()
       ((my-let-1 (v ...) b)
        #'(let ((v 1) ...) b))))

Now we add a check for duplicate identifiers in (v ...).


(define-syntax (my-let-1 stx)
   (syntax-case  stx ()
     ; error cases
     ((my-let-1 (v ...) b)
      (check-duplicate-identifier (syntax->list #'(v ...)))
      (let ([the-duplicate-identifier
             (check-duplicate-identifier (syntax->list #'(v ...)))])
        (raise-syntax-error
         'my-let-1
         (string-append
          "In a use (my-let-1 (v ...) b) the identifiers v ... "
          "must be different.\n"
          "Found a duplicate identifier: ")
         the-duplicate-identifier)))
     ; proper usage
     ((my-let-1 (v ...) b)
      #'(let ((v 1) ...) b))))

(my-let-1 (a b) (+ a b))
(my-let-1 (a a) (+ a b))


Now the error reads as follows:

   my-let-1: In a use (my-let-1 (v ...)  b) the identifiers v ...
             must be different.
             Found a duplicate identifier:  in: a

And the corresponding a is highlighted in the code.

Advice: Place the error checking cases first, then
         the "work horse" cases.

-- 
Jens Axel Søgaard



Posted on the users mailing list.