[Grace-core] More thoughts on modules

Kim Bruce kim at cs.pomona.edu
Tue Jun 7 17:14:17 PDT 2011


More on modules, responding to Andrew's suggestion about using parameters to import modules:

Allowing for changes in names when importing can be tricky.  The idea in my module system is that one would import the interface (or definition) modules, which make no claim on implementation.  The advantage of this is that the interface modules provide all the information one needs to type-check the new code in the presence of the imported code.  If one were to write modules as taking other modules (or even pieces of other modules) as parameters, you would have to provide all of the kinds of information that is in the definition modules already, making it pretty clunky.  Personally I’d rather import from interface modules and then allow renaming when writing an implementation module for a given interface module.

Let’s take a very simple example (using interface as the keyword):

Interface Module DMod {

    type T {...}  // alternatively can also hide some methods, giving a partial revelation:
                         // (I used different names, T and U, to make it clearer this time)

     type U extends {
           m:....
           ...   // list only features you want publicly available outside of module
     }

    const o: U    // list type, but not implementation

    const makeT: S -> T  // closure, not a method, as it’s not sitting in an object.
}

(As Andrew points out, rather than exporting makeT as a closure, we could instead wrap it in an object and export it.  I have a marginal preference for exporting closures, but either works.  I originally had a variable also being exported, but instead we could export an object like o with getter and setter methods for the variable.)

Implementation Module implements DMod {

     import EMod  // options would exist to import either all or some of it,
                             // could also specify whether need to use qualified names or not

 

    type T = EMod.OT

    type U = EMod.OU

    const o = EMod.oo

    …
}

This implementation module simply does renaming to make the imported module match up with the names in the definition module.  Now we can link this in as the implementation module corresponding to DMod.

This seems to work pretty simply if we have structural types, as T is now just another name for EMod.OT, so all type specifications in EMod could have their OT’s replaced by T’s without difficulty.  If we had nominal types then we’d have a way to define types so that we ended up with the new name being an alias for the old rather than giving a “new” type (and hence different type), which would not be freely substitutable for the old.

Now when we import DMod, we would end up getting those items from the EMod implementation, with their names changed.

If someone would like to work instead with parameters to modules, I’d be interested in seeing how one would write an example like this so that there is enough information that we could do static type-checking of the importing module (before we fill in the formal parameters with the actual parameters).

Please note that all of the above on modules is written from the perspective of statically typed languages.  A quick read of Andrew’s paper seems to indicate that similar effects could be obtained by having objects provide (at least) two different encapsulation properties.  One inside the implementation module that would provide calling privileges to all methods of the object.  A different one would be provided for each interface, specifying that only the methods listed in the interface module would be callable from objects that don’t live (or are generated) in the implementation module.  Andrew, does this make sense?

Kim




More information about the Grace-core mailing list