At Wed, 14 Dec 2016 18:12:52 -0800 (PST), Dupéron Georges wrote: > Hello! > > This is a follow-up to the excellent answer by Alex Knauth on StackOverflow > here: http://stackoverflow.com/a/38032107/324969 and this mailing-list > thread: > https://groups.google.com/forum/#!topic/racket-users/sS-pUUGp9o4 > > I'm trying to write a module meta-language mylang, which accepts a second > language to which is passes the modified body, such that: > > (module foo mylang typed/racket body) > > is equivalent to: > > (module foo typed/racket transformed-body) > > The real-world goal is to transform the fully-expanded body (e.g. add a > submodule, like the "doc" module added by scribble/lp2). > > Alex came up with the implementation below. It puts the body into a dummy > module, fully expands it, and extracts the (#%plain-module-begin . mod-body), > patching it to require the user-provided lang. This works very well in all > cases except when the body contains submodules.
The problem here is somewhat related to the thread about syntax objects in properties. When a module expansion completes, bindings in syntax objects are shifted so that a binding from "the module currently being expanded" is redirected to be a binding from "this module". If you re-expand a whole module, then "this module" is shifted back to "the module currently being expanded"; at the end of the at re-expansion, it's shifted back to "this module", and so on. The problem is that you're already inside one module, the so the expanded module's content is not shifted from "this module" to "the module currently being expanded" as the module-body expansion continues. That absence of a shift turns out not to matter for most things, but submodules the second time around are instantiated as relative to "the module currently being expanded". Meanwhile, unshifted references remain relative to "this module". I think you probably want something like the variant below. It's similar to Alex's strategy, but it wraps the body in a `#%module-begin` form, instead of `module`, so that you can use 'module-begin with `local-expand`. That way, you stay within the same overall module expansion. The main extra trick in the implementation is a trampoline via `continue` to first `require` the language to get its bindings, including `#%module-begin`. #lang racket ;; The language definition (module mylang racket (provide (rename-out [-#%module-begin #%module-begin])) (define-syntax (-#%module-begin stx) (syntax-case stx () [(_ lng . rest) (with-syntax ([#%module-begin (datum->syntax #'lng '#%module-begin)]) #`(#%plain-module-begin (require lng) (continue #%module-begin . rest)))])) (define-syntax (continue stx) (syntax-case stx () [(_ lang-module-begin . rest) (let ([body-stx (local-expand #'(lang-module-begin . rest) 'module-begin (list))]) ;; pattern-match on the #%plain-module-begin form to insert a require (syntax-case body-stx (#%plain-module-begin) [(#%plain-module-begin . mod-body) #`(begin . #,((make-syntax-introducer) #'mod-body))]))]))) ;; A module using that language (module foo (submod ".." mylang) typed/racket/base (module a-submod typed/racket/base (define x 1) (provide x)) (require 'a-submod) (ann (+ x 1) Number)) (require 'foo) -- 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.