You can see a simpler version of this behavior with this tiny macro:
(define-syntax m
(syntax-parser
[(_)
#:with x 42
#'x]))
(m)
This produces the same “literal data is not allowed” error message. Why?
In Racket, literal data inside a syntax object — that is, anything that
isn’t a symbol or a pair — is not legal anywhere in a fully-expanded
program. These things are only legal in a fully-expanded program when
wrapped in quote or quote-syntax, so this is a valid fully-expanded
program:
(define x (quote 42))
...but this is not:
(define x 42)
Of course, when writing code in #lang racket, we enjoy so-called
“self-quoting” literals, so booleans, numbers, characters, strings,
bytes, regexps, vectors, hashes, and prefab structures are all legal
anywhere in expression position. How does this work if I just said
literal data is illegal when not explicitly wrapped in quote? Well, to
support self-quoting literals, the macroexpander implicitly wraps any
literal data it finds with #%datum, as described here:
http://docs.racket-lang.org/reference/quote.html#%28form._%28%28quote._~23~25kernel%29._~23~25datum%29%29
http://docs.racket-lang.org/reference/syntax-model.html#%28part._expand-steps%29
In #lang racket, #%datum just expands directly to quote, but in some
other languages, that might not be the case. My own #lang hackett, for
example, does not support all of the literals #lang racket does, so it
provides a custom version of #%datum that raises a syntax error when the
user tries to write things like prefab structure literals in Hackett
code. But how does the macroexpander figure out which #%datum to use?
Well, it uses the lexical context of the piece of literal data itself.
This is a problem, since in your code (and in my example program above),
you are binding a piece of literal data to a pattern variable using
#:with, which implicitly promotes the data to a syntax object. The
binding clause in my example program is equivalent to doing this:
#:with x (datum->syntax #f 42)
This means that the syntax object bound to x has (1) no lexical context
and (2) no source location information. The former means that the use
of 42 is an error ( sincethe expander doesn’t know which #%datum to use)
and the latter makes reporting an error somewhat difficult (since the
expander has no idea where the 42 came from). There are three
straightforward ways to fix this.
1. Use syntax or quote-syntax to explicitly create a syntax object.
This is the easiest thing to do if your program is really as
trivial as the example program at the beginning of my email. Just
change the binding clause to this:
#:with #'42
...and now the 42 will have the surrounding lexical context, the
correct #%datum will be bound, and everything will work.
Of course, in practice, you are often producing the number via
some arithmetic expression or other function call, so you can’t
just use syntax or quote-syntax. In that case, the following two
approaches apply.
2. Use datum->syntax to provide the correct lexical context.
This binding clause allows using an arbitrary number while still
providing enough lexical context for the expander to pick the
right #%datum:
#:with (datum->syntax #'here 42)
If it makes sense, you can even provide a third argument to
datum->syntax to provide source location information, but that
likely isn’t necessary.
3. Explicitly wrap the use in quote.
It’s possible to solve this problem without fiddling with x’s
lexical context at all, since you can just ensure x is only used
under quote in the expansion:
#:with x 42
#'(quote x)
This works fine, since literal data is allowed under quote,
regardless of its lexical information.
Using either of the second two solutions should help you solve your
problem.
Alexis
P.S. As a final note, this behavior reflects a minor difference between
syntax-parse and its #:with clauses versus syntax-case and the
with-syntax form. If you use with-syntax instead of #:with, you’ll
notice there is no error. This is because syntax-case and with-syntax
both implicitly use the lexical context of the expression that produces
a value to be bound when implicitly converting data to syntax objects,
whereas syntax-parse creates a syntax object with no lexical context at
all.
The syntax-case behavior makes some of these problems go away, but it’s
also a little more magical (in a negative way), and it has the potential
to hide some bugs, so I think the choice syntax-parse makes is the right
one.
P.P.S. As a really final note, uses of unsyntax inside quasisyntax also
has the same implicit behavior as syntax-case and with-syntax, so #`#,42
will actually produce a syntax object with lexical context, not
(datum->syntax #f 42).