[racket] implementing "require-redefine"
On 11/09/2011 07:29 AM, Ismael Figueroa Palet wrote:
> Hi all, please help me with the following:
>
> I need to require a module, ideally with the same syntax as require,
> then access to all the imported symbols and re-export them with the same
> name but with a new definition, that is a wrapper for the original one.
>
> I think this should be done with a macro, but I don't know how to access
> the imported symbols acquired with require.
>
> any pointers to documentation or ideas on how to implement this?
You'll want to implement something similar to `all-from-out'. I don't
know exactly how it works; specifically, how it gets a list of imported
identifiers. It's a "provide transformer" - you can search for that term
in the docs. Also, don't be afraid to look at the code for
`all-from-out'. It's defined in "collects/racket/private/reqprov.rkt".
Random tips:
You'll need to make a provide transformer (using
`make-provide-transformer) or a provide "pre-transformer" (using
`make-provide-pre-transformer'). Pre-transformers are expanded first,
and return new provide syntax. Transformers are then expanded, and they
return "exports", which are structs that contain information about
provided symbols. In either case, you can get away with just
manipulating syntax.
Unlike regular macros, provide macros don't automatically expand until
they expand into a basic form. If, say, in a pre-provide macro, you
return new provide syntax that's *not* a basic provide form, you'll need
to use `pre-expand-export' to expand it. There's a corresponding
`expand-export' for provide macros.
To make a wrapper, use `syntax-local-lift-expression' to lift the syntax
of the wrapper's definition to the module level. You can then export it
by having a pre-provide macro return `(provide wrapper-id)'.
In dealing with requires and provides, you'll need to deal with phases.
The mysterious "modes" mentioned in the docs on require and provide
transformers are relative phases. I've found the following function
helpful in a few places:
(define-for-syntax (phases->abs-phases phases)
(map (λ (phase) (and phase (+ phase (syntax-local-phase-level))))
(if (null? phases) '(0) phases)))
It turns relative phases into absolute phases, and ensures that there's
always at least one. You'll want this if you want to look up identifiers
to ensure they're defined, or lift a provide (so you can wrap it
properly in a `for-meta'). If you can stick to just syntax
transformations in your provide macros, you won't need it at all - just
pass the relative phases through to `expand-export' and `pre-expand-export'.
In existing provide macros like `all-from-out', you'll see "(apply
append (map (lambda ...) ...))" in a few places. These map over
identifiers and phases, and collect all the results. I've found
"(append* (for/list ...))" and "(append* (for*/list ...))" are cleaner.
(Of course, in "reqprov.rkt", the `for' macros haven't been defined yet.)
I think this should be enough to get started on.
Neil T