> Jay McCarthy writes:
>> Modules don't evaluate to values. They have effects and they have
>> exported symbols. If you want to observe the evaluation of your
>> language's module, you'll have to look at one of those two things.
>> Both are used by existing Racket languages and infrastructure: `raco
>> test` relies on test modules making effects on a global box that
>> counts how many tests ran and failed. `scribble` relies on inspecting
>> an export named `doc`. In either case, I think you want to make
>> `#%module-begin` capture the last expression and expose its value via
>> an effect or an export.
Christopher Lemmer Webber writes:
> Well I gave it a try and couldn't quite figure out how to make it work.
> I tried writing out this file, dungeon/room-ch.rkt:
>
[...]
> It just hangs, so I assume that the module never wrote to that channel.
> I guess it probably wouldn't until it's required, but I have no idea how
> to "require" this dynamically-read-in module to prime it?
Wow, ok. So I figured, "this must be possible to talk to a module
loaded at runtime, because I see in sandbox.rkt that when the user
specifies a lang to the sandbox that it makes a module":
(define (use-lang lang) `(module program ,lang . ,body))
So I figured that ok, there must be some way to evaluate a module and
get it to "run", and it must be taking either of the approaches Jay
mentioned. So I spent about an hour digging around in sandbox.rkt and
taking notes thinking I'd find out where it did that.
Well... I was in for a surprise. My assumptions appear to be wrong.
As far as I can tell:
- sandbox.rkt first sets up a fresh namespace to do the evaluation in
(ok, expected)
- Then later in evaluate-program:
(define ns
(syntax-case* program (module) literal-identifier=?
[(module mod . body)
(identifier? #'mod)
(let ([mod #'mod])
(lambda ()
(for ([submod-name (in-list submod-names)])
(eval `(when (module-declared? '(submod (quote ,mod) ,submod-name) #f)
(dynamic-require '(submod (quote ,mod) ,submod-name) #f))))
(module->namespace `(quote ,(syntax-e mod)))))]
[_else #f]))
sandbox.rkt is then piecing apart the module and taking the module
path and adding it to the namespace?! Or at least preparing to,
since this is wrapped in a lambda to be run later.
- It actually appears that the program (the whole `(module ...)`
structure is actually just eval'ed first, and *then* I guess the
namespace is set up:
(if (and (pair? program) (eq? 'begin (car program)))
(eval* (cdr program))
(eval program))
(when ns (set! ns (ns)))
- At the end of it the current-namespace is set to ns if indeed
evaluate-program is dealing with a (module ...) thing:
(when (namespace? ns) (current-namespace ns))
- Then later on when the user uses the sandboxed evaluator and passes
in expressions, I guess that namespace is used and evaluation takes
place in it.
That is NOT what I was expecting. What I figured when I saw the
`(module program ,lang . ,body) stuff was that the module would be
evaluated on some sort of very generic
module-dynamically-loaded-at-runtime way. That doesn't quite seem to be
the case.
This was a useful read; I'm still unsure if I'm reading it right
though. I guess it seems to say "this tooling doesn't quite exist out
of the box, but you can assemble something resembling it on your own"?