Reputation: 22332
Let's say I have some module foo.rkt
that provides x
at phase 1.
#lang racket
(begin-for-syntax
(define x 5)
(provide x))
When you run (module->exports "foo.rkt")
you get back ((1 (x ())))
, meaning that x
is provided at phase 1 and no other bindings are provided.
Now, in another module I could statically import x
during run time using for-template
:
#lang racket
(require (for-template "foo.rkt"))
x ; => 5
But this is static, and so it will always happen.
If this was at phase 0 I could use dynamic-require
. But it seems like you can only use dynamic-require
to run phase 1 code, not get any values from that ran code.
There is also dynamic-require-for-syntax
, but I could never manage to work.
Finally, there's also namespace-require
, but then it brings it into the namespace's phase 1, rather than phase 0. So I could do something like (eval '(begin-for-syntax (writeln x))
, but that will only print the value of x
, not return it.
There's also namespace-variable-value
, but it also only seems to return values at phase 0.
So, is there anyway that I can dynamically (not statically) import a phase 1 variable from a module?
Upvotes: 2
Views: 204
Reputation: 22332
Yes there is a way, but its kind of disgusting.
First of all, we need to make a base namespace, so something like (define ns (make-base-namespace))
will do the trick.
Next, I would actually recommend using namespace-require/expansion-time
rather than namespace-require
. It will only instantiate the module (aka only run phase 1 code).
Doing this, x
is not imported into the namespace, but at phase 1, so we can write a macro to 'smuggle' it from phase 1 to phase 0 through 3d syntax.
The macro is going to look something like:
(eval '(define-syntax (cheater-x stx)
#`'#,(datum->syntax #f x)))
And now you can just do (eval 'cheater-x)
to get the value of x
.
Overall your code should look something like this:
(define (dynamic-require-from-syntax module binding)
(define ns (make-base-namespace))
(parameterize ([current-namespace ns])
(namespace-require 'racket)
(namespace-require/expansion-time module)
(eval `(define-syntax (cheater-x stx)
#`'#,(datum->syntax #f ,binding)))
(eval 'cheater-x)))
(dynamic-require-from-syntax "foo.rkt" 'x) ; => 5
Obviously you could set up this function to use the same namespace on multiple calls so that it doesn't re-instantiate the module every time you call it. But that's a different answer.
Upvotes: 2