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

Reply via email to