[racket] implementing "require-redefine"

From: Neil Toronto (neil.toronto at gmail.com)
Date: Wed Nov 9 11:50:18 EST 2011

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


Posted on the users mailing list.