> On Aug 17, 2017, at 21:52, Sam Waxman <samwax...@gmail.com> wrote:
> 
> On a related note, I've read that read-syntax is supposed to return a
> syntax-object whose lexical context is stripped. Why is that? Doesn't
> that make it impossible for the language to know the difference
> between the let I used in an earlier file and the let that the user
> types as an identifier?

Yes, this is likely the cause of your problem (and it’s what I guessed
was probably going on before you sent the second post). This is, for
better or for worse, currently a no-no in Racket — syntax objects
produced by a #lang’s reader are supposed to only have source locations
and syntax properties on them, not lexical context.

There are various reasons this can be explained, some historical, others
technical. One explanation I’ve heard is that valid programs read by
`read-syntax` are supposed to also be valid programs if read by `read`
— this means lexical context shouldn’t matter. In practice, I think this
is probably not a very strong argument (I think `read`’s existence and
its relationship to the #lang protocol is mostly a historical artifact
in Racket), but there are also questions about the meaning of syntax
objects with lexical context that have not yet been seen by the
macroexpander. Those technical details are outside of my realm of
knowledge, but I can at least offer some solutions.

Generally, my recommendation is to essentially define your language in
two passes: a direct translation to s-expressions, followed by a phase
of macroexpansion. The first phase is what your reader interacts with.
Give the primitives that your reader produces names that are unlikely
to conflict with users’ code — specifically, prefix them with “#%” so
that they are clearly special. It can also be useful to include
characters in the identifier names that aren’t even valid identifier
characters in your language, but this is not always possible if any
character can be legally used in an identifier. Either way, this means
your my-let macro should likely be named something like #%let or
#%my-language-let, and your reader produce syntax objects without
lexical context that use these #%-prefixed primitives.

The good news is, once you have converted your #lang’s source syntax to
s-expressions, you can hand those expressions off to the macroexpander,
and then you can use whatever hygienic names you wish. You can define
your #%-prefixed primitives as hygienic macros that expand to
nicely-scoped syntax objects with lexical context. For example, in a
non-s-expression language I implemented in Racket, I converted lambda
syntax into uses of a #%lambda form, and since the language supported
pattern-matching, I defined #%lambda as a macro that expanded to plain
old match-lambda** from racket/match. At that point, the name will not
conflict, because macroexpansion is hygienic.

It feels a little inelegant that Racket’s hygiene system does not extend
to read-time, since it means the sort of hacks necessary in unhygienic
languages are sometimes necessary in Racket when implementing a reader.
Fortunately, a reader is much more self-contained and smaller in scope
than a macro-enabled language, so it usually isn’t a big deal. Perhaps
some language will eventually motivate a hygienic reader layer, but
Racket doesn’t current have one.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to