Modules

Matthew Tuck matty@box.net.au
Mon, 10 May 1999 21:41:22 +0930


Hans-Dieter Dreier wrote:

>> Why would you want to know that something is conditionally compiled?  If
>> I use a normal if, it can optimise it out or not, it doesn't affect me
>> for the most part.
> If I see an #if (and know its value) I know definitely which way control will
> flow.If it is a normal if, I can't be so sure - I first have to check whether its
> value is constant and known.

Well, you don't definitely know which way it will flow, you only know
that it will definitely be known at compile time.  You can't see that
from an if, instead you have to understand what the identifier
represents.  But the constancy should usually be obvious from the name,
e.g. IS_UNIX.
 
>> The essential difference is that there is only one instatiation of
>> classes.  Also, classes can usually not contain other classes, although
>> in Java's case, it is allowed.  Hence the need for the distinction is
>> disappearing.  A "unified containment hierachy language" only uses
>> classes (or types/impls), and considers packages to be "singular
>> objects", that is a special sort of combined class/object.  Therefore
>> there is only one mechanism for information hiding rather than two.  Ada
>> is a mess in this respect.
> I wouldn't misuse packages for information hiding.
> ...

I wasn't implying that.  Classes (possibly augmented by shorthands) are
used as the only namespace mechanism, rather than there being two
sorts.  Modules are used as a packaging concept, hence the concepts
become more independent.  Basically like as follows:

                             Java/Ada    Mine
Used For File Separation     Packages    Modules
Used For Libraries           Packages    Classes (Singular Objects)
Used For Classes             Classes     Classes

It's kind of a transfer of responsibility.

I'll try to read the paper shortly.  I remember reading a couple of
those papers before.

> Oh no! Just display a cross reference for information purposes if
> wanted.  Let the editor/compiler take care of uses.

That's what I'm in preference of at the moment.

>> (b) To require the programmer to specify redundant information about
>> what they intend to use?
> No, too much hassle. What would be gained?

Well, the same as any redundant information - to try and prevent errors
by catching inconsistent programming.

>> (c) To make it readable as to what other elements are used without
>> looking at the body?
> Can be provided on demand by the editor.

Yes.

>> (d) If included in types, could be used to restrict what elements a
>> defined method can implicitly access.
> What should that be good for?

It could be used to prevent it from calling certain methods.  It's
basically like a sandbox, ie a set of policies about what other elements
a element can access and in what ways.

A sandbox is usually only applied to loaded/generated classes, although
it could become a language entity.  Whether this would be worthwhile, I
don't know.  And how sophisticated to make it would also be a question.
 
>> That's a good point - the code may change outside of the editor.  If
>> it's read-only, it's OK, but if you can write to the code, we could get
>> problems.  If we have a lock facility to make it read-only if someone
>> else is editing it, that would work, but what about if not?
>> We probably should attempt to ensure files are not saved if they aren't
>> edited, although this might not be easy given my desire to make them
>> kind of implicit.  Or it might be dead easy.
> Every decent editor does it.

It's probably quite doable.  We just have to make sure that when a
change is made through a view, which would usually not care about
modules, the editor determines which file this affects and marks it as
changed since save.  The complication is that a normal editor usually
only shows a file per window, whereas I'd prefer it in one window.

That does lead me to comment though that a nice view option would be to
restrict a view to only showing one module.
 
> I'll try to elaborate a little bit:
> ...

OK, so this essentially requires you to enter dependencies on a per
module basis rather than working them out for themselves.

It would certainly be useful to view the used elements/modules in any
module, as well as in any language element, e.g. in a method rather than
a whole class.  Probably not within the main window though for a
language element, instead in a dialog.
 
> I'd like to emphasize that the names of all include files are in one place
> and are not scattered about like it is in C.
> You'll never have to search for things that have been included.
> To the user, it looks as if everything is in one big file, which has some
> read-only parts.

That's a good point.  The ability to centralise things is very useful,
and we should endeavour to provide means of extracting information into
a meaningful view rather than requiring the programmer to hunt and peck
for information distributed throughout the program.
 
> Actually, the standard file format (binary) saves a copy of all the included parts
> along with the top-level module.

Do you mean the code from other modues, or just all the information
necessary to generate the code?

> This results in extremely fast load/save cycles (and in huge files as well,
> but who cares?) and has the added benefit that you need only transfer one
> file if you wish to copy a project. If the included files are not present
> at the copy location, the load function will simply complain and continue
> to use the existing copy. Otherwise it would have checked the file dates to
> see whether a refresh was due.

Well it seems like it could be useful to be able to work in the absence
of the other files, but would you need this often in practice?

>> No, nothing as complicated as that.  Every detached element will act
>> exactly as if it was attached at run-time.  You would essentially just
>> compile the detached element in the namespace of the other element, then
>> later reattach it at linking stage.
> How can you attach a feature to a *class* at link time?Isn't that VERY
> complicated? You need to recompile all derived classes because
> the instance layout may change.

It was never intended to get around that problem.  But you're right - it
is difficult, so it might well be worthwhile just doing something like
this with abstract impls and impl inheritance if you want them in
different modules, and hoping inheritance optimisations will merge the
two impls together if possible.

I'm actually not aware of any compilers that do inheritance
optimisations, but they seem to me to be a very useful if they can be
done.  By this I mean things like merging two impls together if the
parent impl is only used by one subimpl, doing the same for types, and
removing multiple inheritance where it's unneeded.

These are based on the premiss that you might have separate entities for
good reason, but in one particular program they might not need to be
separate.  And yes, they would usually need to be inter-module.

As an example take the following (implementation) inheritance hierachy.

      Abstract A      Abstract B
     /          \    /
Concrete C    Concrete D

Consider this to be closed, ie we're guaranteed nothing else will extend
these implementations.  Note the impl MI on D.

Since B is only used by D, we know that Bs methods will only be used in
D.  Hence, any methods not used by D can be eliminated, and anything
used by D can be inlined into the body of D, or become Ds methods if
they are inherited but not overridden (which is really a simple case of
inlining), and then eliminated from B.  Once all of the methods of B are
eliminated, B can be eliminated.  Hence there is one less element in the
system, and there is no MI.

Types are probably a little more complicated, since they can be used
both in variable declarations, impl declarations and type declarations
rather than just creations and impl declarations.

An alternative system for doing submodules would be more conventional. 
That is, a particular subtree is specified to be in another module, eg

/
| Module A
| 
|  /
|  | Module C
|  \
\

/
| Module B
\

The modules can nest to arbitrary depths, and there is no "detaching". 
Again, you would want to modular boundaries to be visible or not
according to user preference.  It gets a little trickier, since it is no
longer a matter of specifying a list of modules, but rather you have to
say C goes into A - kind of like mount points in Unix if you're familiar
with them.

-- 
     Matthew Tuck - Software Developer & All-Round Nice Guy
             mailto:matty@box.net.au (ICQ #8125618)
       Check out the Ultra programming language project!
              http://www.box.net.au/~matty/ultra/