loading modules

Mark P Jones mpj@cse.ogi.edu
Fri, 7 Dec 2001 11:17:07 -0800


|  > * When you evaluate an expression in the interpreter, it has
|  >   to use some symbol table for looking up the ids you use. What
|  >   symbol table does it use?  The only credible alternatives are:
|  >
|  >     * The export list of the "current module" (see :module command)
|  >     * The symbol table of the "current module"
|  >
|  > Hugs uses the latter (which seems more useful) but you might reasonably
|  > expect Hugs to use the export list and wonder why Hugs doesn't seem to
|  > implement abstract data types correctly when you do experiments from
|  > the command line.
|
| This begs the question of exactly what it means to "load
| modules". The Hugs documentation AFAIK seems to assume it's
| self-evident. But, as the "not a bug it's a feature" message
| above makes clear, it isn't, even when just a single module
| is loaded. I would have thought module loading could be
| defined in terms of something *in* Haskell (as did presumably
| the bug reporters the message is responding to); but at the
| moment the best that could be said is that module loading is
| an undocumented hack.

I disagree.  The behavior is documented (see the description
of the :module command), although I'll agree that it is not
labored to the extent that you might have liked.

If the Hugs prompt indicates that you are *in* a module M, then
the symbols that are in scope are precisely those symbols that
are in scope *in* the module M.  The details of an "abstract
data type" are hidden only in clients of that type, and not
within the implementation.  If you want to observe the effects
of working with an abstract datatype that is defined in a module
M, then you can define a new client module something like the
following

  module ClientOfM where
  import M

and then do a :load ClientOfM to put yourself in the desired
context.  This is not a bug/feature issue as you describe it; it
is how Hugs reflects the differences between the implementation
and the clients of an ADT.  In fact there are important concepts
to learn from the fact that some expressions will be accepted
when the current module is M, but rejected when you switch to
ClientOfM.

One could imagine extending an interpreter like Hugs with a
more complicated :module command that allows users to specify
on which side of the module boundary they wanted to be.  For
example:
  :module <M
might put you inside M, while
  :module >M
might put you just outside with only exported entities in scope.
I don't think the extra complexity is justified, because the same
thing can be accomplished, without having to step outside the
language, by defining modules like ClientOfM.  And indeed, when
you do that, you have much more flexibility, for example, to import
multiple modules, to use hiding, selective, or qualified imports,
and even to add extra definitions for use in testing or experiments.
And by capturing this in a persistent form (i.e., in a file), you
can use the same thing many times, share it, etc...

The ability to execute an expression within the context (or the
"symbol table" as the poster you reference calls it) of the current
module is particularly useful for debugging or otherwise working
with the internals of a module.  The other alternative that was
suggested (i.e., that expressions be evaluated in the context of
the export list only of the module named in the prompt) would not
support this kind of behavior and would, in my opinion, be a weaker
design.

All the best,
Mark