Modules

To build large systems, need to split code into separate chunks.

tex2html_wrap_inline155 Separate name spaces to avoid name clashes.

tex2html_wrap_inline155 Separation of specification from implementation to support abstraction.

tex2html_wrap_inline155 Separate compilation of portions of the system to speed development.

Standard ML supports these with a module system comprised of structures, signatures, and functors.

Not specifically tied to functional programming.

Currently being revised for new version of SML.

Moscow ML supports only a simpler, file-based module system with no functors.

Structures and Signatures

Basic notion of a module is a collection of types, functions, and values grouped together into a named structure.

code48

Each structure has a signature; in this case, an (anonymous) default one reported back by the system. The signature is a list of type declarations and typed value declarations. More later.

Referring to Structure Components To refer to the members (types and functions) of the structure from outside its definition, we use dot notation:

code54

To make such references, Set must already have been defined.

If we're going to make lots of use of a particular structure, we can open it, allowing its members to be referenced without naming the structure.

code58

Of course, this removes the benefit of each structure having a separate namespace, so open should be used sparingly. Since open behaves like a declaration, it can be used in a let or local, and this is generally preferable to a top-level use.

Libraries

The SML standard library is organized as a set of tt structures some pre-opened at top level and others not:

tabular66

We can refer to individual elements of these structures (whether already open or not) using the dot notation. We can also open any of them; note that re-opening something like Integer will destroy overloading.

Similarly, the New Jersey library is a further set of structures. Note that the names of files containing structure definitions don't actually matter, although by convention we usually put one structure in one file with a similar name. (Moscow ML differs.)

More on Signatures

A signature is a description of the contents (types, typed values) of a structure, but says nothing about the implementation of the structure.

Signatures can exist independently of structures, and are useful in their own right as a way of describing interfaces or specifications, e.g., for abstract data types.

code87

Note that signatures can be named or anonymous.

Signature Constraints

Signatures (named or anonymous) can be used to control which parts of a structure definition are to be visible outside the structure body:

code92

We can also use signature constraints to define multiple views of a single structure:

code95

Rule for constraints: structure must contain everything required by signature, but may contain more.

The default signature for a structure exports everything.

Abstraction Properties

The module system provides three possible levels of abstraction for datatypes:

If signature itself specified a datatype, the structure must have an identical datatype definition, and constructors are fully visible outside.

code99

Abstraction Properties (Cont.)

If signature specifies a type, structure can implement this type in any way it likes, and constructors will not be visible outside, but name and properties of type will be (somewhat similar to a local declaration).

code104

To make properties of type completely invisible, use the abstraction keyword in place of structure:

code108

Generally, should use abstraction instead of abstype.

Functors

Often we'd like to write (and compile) code that uses a module specification (signature) even if no implementation (structure) of that specification yet exists. We can do this in ML using functors, which are just structures that are parameterized on other structures, values, or types.

For example, suppose we are writing a application involving sets, but don't want to commit to a particular implementation of the Set structure. We write:

code118

This can be type-checked and compiled, even if there are no structures matching HiddenSet yet defined.

Later, we can write one or more implementations of HiddenSet:

code121

Functors (cont.)

Then we may choose to apply the functor to one (or both) of the HiddenSet implementations.

code126

Functors are useful wherever abstraction at the module level is wanted.

Example: Recall the problem of implementing sets using ordered trees, where all set operations need to be parameterized by a ``less-than'' function.

One approach is to write the Set structure as a functor, parameterized by the underlying type and its lt predicate.

Parameterized Set Example

code131

Core Language vs. Module Language

There is a close correspondence between the basic elements of core ML and the module language:

Values tex2html_wrap_inline161 Structures

Types tex2html_wrap_inline161 Signatures

Functions tex2html_wrap_inline161 Functors

Not too wrong to think of structures as records of values, etc. (although modules can also have type components).

But modules are static entities; all module-level calculations are performed before the program is ``run.''

Functors are similar to C++ templates, but in most implementations functor body is compiled just once; functor application can be thought of as a link-time step, and functors as an elaborate linking control language.


Andrew P. Tolmach
Tue May 20 14:41:49 PDT 1997