[racket-dev] Difficulties building a custom expander

From: Carl Eastlund (cce at ccs.neu.edu)
Date: Fri Oct 21 21:35:51 EDT 2011

I've run into a few difficulties in building a language whose modules
are expanded primarily through local-expand.  I thought I'd share them
in the interest of improving Racket's expansion mechanisms in the long
run (or if they can be fixed in the short run, even better).  So far I
don't believe any of them are insurmountable, but they do constrain my
design and implementation.

First of all, local-expand only allows me to introduce lexical
bindings (not module-level bindings, for instance).  Lexical bindings
limit each identifier to only one binding total, not one per phase.
My language therefore cannot have its own "define" at phase 0 and
still use Racket's "define" at phase 1 inside macros.  I instead have
to require (prefix-in racket: racket/base) at phase 1, which is
hideous to actually use.

I also cannot allow the user to make their own phase 1 bindings via
begin-for-syntax or for-syntax requires, because there does not seem
to be a way to add phase 1 bindings from a phase 0 transformer.

Manipulating bindings using syntax delta transformers to transfer
syntax marks is also unnecessarily difficult.  There are two utilities
for constructing deltas: make-syntax-delta-introducer and
syntax-local-make-delta-introducer.  The latter computes a delta
between an identifier and its binding, if it is bound as syntax in the
current environment.  The former computes a delta between an
identifier and its binding, if it has a module binding; or it computes
a delta between two given identifiers.  This means that given an
identifier, I can only compute a delta with its binding if (a) it has
a module binding, (b) it is bound as syntax in the current
environment, or (c) I have its original binding at hand.  I would much
rather have a single function that computes deltas for any given
identifier, regardless of what kind of binding it has.  (Some of this
would be less problematic if syntax marks were allowed to commute;
then it would be easier to compute deltas based on two references.  As
it is, deltas really only make sense when an original binding without
additional marks is available.)

Of course, no one should take this as a complaint about Racket
overall; its language-building tools are great.  If they weren't, I
wouldn't be able to have such detailed and esoteric complaints in the
first place.

Carl Eastlund

Posted on the dev mailing list.