Imported syntax class works different from syntax class copy-pasted into file

I'm currently trying to reuse some syntax classes that I've made, and want to match certain identifiers within them. These syntax classes produce an error if I attempt to import them rather than defining them directly within the macro I am writing. This is a small breaking example I could come up with:

This works fine

#lang racket/base

(require (for-syntax racket/base
                     syntax/parse))

(define-syntax (test stx)
  (define-syntax-class test-class
    (pattern ((~literal list) (~var x) (~var y))))
  
  (syntax-parse stx
    [(_ (~var list-expr test-class)) #'(void)]))

(test (list '(a b c) 1))

This produces an error stating "expected the identifier `list' parsing context: while parsing test-class in: list":

;; some-syntax-classes.rkt
#lang racket/base

(require syntax/parse)

(provide (all-defined-out))

(define-syntax-class test-class
  (pattern ((~literal list) (~var x) (~var y))))
#lang racket/base

(require (for-syntax racket/base
                     syntax/parse
                     "some-syntax-classes.rkt"))

(define-syntax (test stx)
  (syntax-parse stx
    [(_ (~var list-expr test-class)) #'(void)]))

(test (list '(a b c) 1))

Any idea what I'm doing wrong?

So ~literal matches an identifier -- including a phase as well as a symbol: It's "list at some specific phase level".

In your first example, where the syntax class is defined at phase 1 inside the define-syntax, the list identifier in (~literal list) is the one at phase 1.

But in your second example, where the syntax class is defined at phase 0, the list identifier in (~literal list) is the one at phase 0.

That doesn't match the list at phase 1 in the expansion of test. So the error.


One thing you could do, in your second example, is change (~literal list) to (~literal list #:phase 1).

As in the following. [Note: Just for my own convenience, I changed your two-file example into one file -- with a submodule m playing the role of some-syntax-classes.rkt.]

#lang racket/base

(module m racket/base ;~= some-syntax-classes.rkt
  (require syntax/parse)

  (provide (all-defined-out))

  (define-syntax-class test-class
    (pattern ((~literal list #:phase 1) (~var x) (~var y)))))


(require (for-syntax racket/base
                     syntax/parse
                     'm)) ;~= some-syntax-classes.rkt

(define-syntax (test stx)
  (syntax-parse stx
    [(_ (~var list-expr test-class)) #'(void)]))

(test (list '(a b c) 1))
1 Like

That solved my problem! Didn't know I could specify a phase level for it, thank you so much for your help

Keep in mind there are various ways to handle this. Some may be more "ergonomic" solutions, if you're writing a lot of this stuff.

For example you could:

  1. Use define-literal-set to name a variety of symbols (list, cons, whatever, etc.).
  2. Use the #:literal-sets option to use that whole set at a certain phase level.
  3. Now in your patterns you can just write list (not even need (~literal list) anymore, much less (~literal list #:phase 1)).

2 other options I can think of:

  • use ~datum. A bit discouraged, but useful when you only care about
    the textual content and don't want to allow it to be renamed (cond
    recognizes else by binding, which means it works through renames but
    is a bit confusing if else is lexically shadowed).
  • create, provide, and consistently use your own binding. If you
    don't need its value, it can be as simple as (define my-marker #f),
    as long as you're consistent about the phasing use as Greg pointed
    out.