Thanks for the leads! I'll look into your suggestions and follow up with my results.
-Nick On Fri, Jun 15, 2012 at 10:54 PM, Ryan Culpepper <r...@cs.utah.edu> wrote: > On 06/15/2012 10:41 PM, Nick Sivo wrote: >> >> Hi, >> >> I have the following code: >> >> #lang racket >> (require mzlib/defmacro) >> >> (defmacro w/uniq (name . body) >> `(let ([,name (gensym)]) >> ,@body)) >> >> (defmacro in (x . choices) >> (w/uniq g >> `(let ([,g ,x]) >> (or ,@(map (lambda (c) `(eqv? ,g ,c)) choices))))) >> >> Naturally, it fails: >> >> expand: unbound identifier in module (in phase 1, transformer environment) >> in: g >> >> Additionally defining w/uniq for-syntax fixes it: >> >> #lang racket >> (require mzlib/defmacro >> (for-syntax racket >> mzlib/defmacro)) >> >> (begin-for-syntax >> (defmacro w/uniq (name . body) >> `(let ([,name (gensym)]) >> ,@body))) >> >> (defmacro w/uniq (name . body) >> `(let ([,name (gensym)]) >> ,@body)) >> >> (defmacro in (x . choices) >> (w/uniq g >> `(let ([,g ,x]) >> (or ,@(map (lambda (c) `(eqv? ,g ,c)) choices))))) >> >> I know this is Racket's phase separation working as intended. Now, I >> appreciate why Racket separates phases as well as the advantages of >> hygienic macros, but I'm not writing new code. I'm trying to enable >> existing code* to run natively in Racket and take advantage of its >> great JIT and some module optimizations (like inlining). >> >> Is there any way to hack the first version to work? One option is to >> build a deeper nest of duplicate macro definitions each time I >> encounter a new one. It seems possible, and it might even work, but >> it sounds awful. I've already written a custom reader and have an >> extensive library of language syntax/parse macros, so I'm open to >> anything. > > > I don't know of a way of doing this within Racket other than running each > definition at every phase that it might be used at. That might not require > duplicating code, though. > > If you could rely on let*-like scoping for Arc module bodies, then you could > do it entirely with submodules (new feature since 5.2.1; see > http://blog.racket-lang.org/2012/06/submodules.html for an introduction). > > The idea would be to use the arc language's #%module-begin macro to > transform > > (module M arc > defn1 > defn2 > defn3)) > > into > > (module M arc > (#%plain-module-begin > > (module S0 arc) > > (module S1 arc > (require (submod ".." S0) > (for-syntax (submod ".." S0))) > defn1 > (provide (all-from-out (submod ".." S0)) > (all-defined-out))) > > (module S2 arc > (require (submod ".." S1) > (for-syntax (submod ".." S1))) > defn2 > (provide (all-from-out (submod ".." S1)) > (all-defined-out))) > > (module S3 arc > (require (submod ".." S2) > (for-syntax (submod ".." S2))) > defn3 > (provide (all-from-out (submod ".." S2)) > (all-defined-out))) > > (require (submod "." S3)) > (provide (all-from-out (submod "." S3))))) > > Each S<N> submodule provides (at phase 0 only) the names defined by all > definitions defn1 through defn<N>. (For S0, that means zero definitions.) > > Each S<N+1> submodule requires S<N> at phases 0 and 1, thus it gets all > definitions defn1 though defn<N> for both run time and compile-time. > > That's if you have strict let*-like scoping. If you want to support mutually > recursive modules, you could try to group contiguous run-time definitions > together by head-expanding (ie, calling 'local-expand' with a stop list) and > accumulating forms as long as they produce 'define-values' forms. I don't > see any way to support mutually recursive functions that have macro > definitions between them, though. > > I also don't see any way to support references from a macro body to > functions defined later in the module. > > This will break 'set!', because variables imported from another module > cannot be mutated. But you can fix that by defining auxiliary setter > procedures and providing proxy set-transformer macros in place of the > variables. > > Mutating a variable (or data structure) in one phase still cannot change the > value in other phases, no matter what you do. In fact, compile-time mutation > won't work very well at all, since each definition is compiled in a new > submodule, which should get its own fresh compile-time state. > > If you have 'let-syntax' or its equivalent (eg, local 'define-syntax'), then > phases 0 and 1 might not be enough, since you can have expressions at phase > 2 (and higher): > > (let-syntax ([m0 > (let-syntax ([m1 <phase-2-expr>]) <phase-1-expr>)]) > <phase-0-expr>) > > If you want to be really *really* crafty, you could try defining your own > version of 'let-syntax', 'define-syntax', etc to create requires for higher > phases on demand by calling 'syntax-local-lift-require'. That is, > > (arc-define-syntax id expr) > > == expands to the result of calling ==> > > (syntax-local-lift-require > #`(for-meta #,(add1 (syntax-local-phase-level)) S<N>) > #'(racket-define-syntax id expr)) > > I hope that helps. (I haven't tried any of this, so there are surely bugs > that need ironing out.) > > Ryan > ____________________ Racket Users list: http://lists.racket-lang.org/users