[plt-scheme] Attaching uninstantiated modules

From: Lauri Alanko (la at iki.fi)
Date: Mon Jul 29 15:47:41 EDT 2002

On Sat, Jul 27, 2002 at 12:53:00PM -0600, Matthew Flatt wrote:
> The notion of "attach" depends on having an instance.

How so? I just want to import a name from one "module namespace" to another.
Since a module can be accessible (ie. requirable) before instantiation
in the namespace where it was defined, why couldn't it be likewise
accessible in some other namespace as well?

> If you wanted to just share the declaration of `m', then the
> declaration can be evaluated separetly in each namespace --- there's no
> difference at that level (but also no sharing of the variables within
> the module).

And there would be duplicates of identical procedures in memory, which is
wasteful.

> I suppose we could introduce the concept of a module instance whose
> body is not yet executed, and attaching such an instance would make
> sense. But that's not much different than, say, attaching a normal
> module instance whose body invokes a unit lazily.

What is the concept of the thing which gets created when I say (module foo
mzscheme ...) then? It is clearly not yet executed, but it can be
instantiated when it is first required. And, if I understand correctly, it
can be instantiated only once.

And if instantiation is so important, why couldn't a module be instantiated
the moment it is defined? A well-behaving module doesn't do any visible side
effects while initializing, and since most module definitions are anyway
only read in when necessary, unused modules still wouldn't get loaded.

Frankly, the module system seems a bit awkward. Why couldn't modules be just
first-class (syntactic) values? Right now a module has to be identified by a
namespace-symbol -tuple, which is not very practical. In addition to the
runtime environment and the expansion-time environment, there is also, in
effect, the "module environment" where module names are mapped to modules. I
don't quite see the need for this third environment. Defining and importing
a module are syntactic operations, so a module should, it seems, be a
syntactic object similar to, say, a structure type.

Actually, I'm not even sure if the distinction between namespaces and
modules is even sensible. To me they seem like fairly similar concepts. The
only difference is that a module can be defined syntactically at
expansion-time while a namespace can only be created programmatically at
runtime. But one namespace's expansion-time can be another's runtime thanks
to eval...

Here are some miscellaneous other issues:

dynamic-require is ill-named. To me at least "require" means "invoke and
import", whereas dynamic-require only invokes a module but doesn't import
its bindings anywhere.

Is there any way to look up a syntactic binding in a namespace? Or access a
namespace's expansion-time environment?

Is there a way to do "namespace-require/copy" normally during expansion
time, ie. is there anything like "require/copy" for the top level and
modules?

Is there a way to make a module visible in another namespace, but with
another name from that which it was originally defined with?

How can I find the real name of a module that has been required in the
"original" top-level namespace via (require (lib ...))? I tried to get
current-module-name-prefix's value during expansion, but apparently its
value is #f in expansion-time environments.


These questions may seem a bit contrived, but eval is the main reason I use
scheme (otherwise I'm more comfortable with ML's and Haskell's typing
discipline), and when I need eval I need it heavily.

Here's what I'm trying to do: my program traverses a directory hierarchy,
reading and executing scheme code in each directory. Some files contain
expressions which evaluate to actual data (structured documents) which the
program eventually processes. Other files contain definitions which are then
visible in the document files, as well as in subdirectories. And of course
we don't want any code in these documents to access any "dangerous"
functions (such as I/O).

When I started implementing this ages ago with guile, things were simple
enough: namespaces and modules were the same thing. So for each directory I
simply created a namespace, imported the parent directory's namespace to it,
evaluated the definitions in it, and evaluated the document expressions and
processed what they returned. And the root namespace contained just "safe
stuff".

With mzscheme things are a bit more complicated. What I'm doing currently is
that I created a single empty namespace, and for each directory I create a
new module expression that requires the parent directory's module, and into
which the definitions are spliced. Then for each document file I create a
new namespace (since I don't want to clutter the main one), attach and
require the current directory's module to it and eval the expressions, and
process what they return. The main namespace acts just as a place where the
modules are collected together, so it seems a bit silly that I have to do
some requiring _there_ when I only really use the modules in other
namespaces.

Still, don't take me wrong: I think that mzscheme 200's new module system is
really great, especially since it takes care to make a clear distinction
between run-time and expansion-time operations. I'm just saying that its
dynamic API isn't as practical as it could be.


Lauri Alanko
la at iki.fi



Posted on the users mailing list.