ArticlePDF Available

Abstract

We describe a general module language integrating abstract data types, specifications and object-oriented concepts. The framework is based on the Standard ML module system, with three main extensions: subtyping, a form of object derived from ML structures, and inheritance primitives. The language aims at supporting a range of programming styles, including mixtures of object-oriented programming and programs built around specified algebraic or higher-order abstract data types. We separate specification from implementation, and provide independent inheritance mechanisms for each. In order to support binary operations on objects within this framework, we introduce "internal interfaces" which govern the way that function components of one structure may access components of another. The language design has been tested by writing a number of program examples; an implementation is under development in the context of a larger project. 1 Introduction This paper describes a general module sys...
An extension of Standard ML modules with
subtyping and inheritance
John Mitchell
Dept. of Computer Science
Stanford University
Stanford, CA 94305
mitchell@cs.stanford.edu
Sigurd Meldal
Dept. of Informatics
University of Bergen
Bergen, Norway
sigurd@eik.ii.uib.no
Neel Madhav
Dept. of Computer Science
Stanford University
Stanford, CA 94305
madhav@cs.stanford.edu
Abstract
We describe a general module language integrating ab-
stract data types, specifications and object-oriented con-
cepts. The framework is based on the Standard ML module
system, with three main extensions: subtyping, a form of
object derived from ML structures, and inheritance prim-
itives. The language aims at supporting a range of pro-
gramming styles, including mixtures of object-oriented pro-
gramming and programs built around specified algebraic
or higher-order abstract data types. We separate specifi-
cation from implementation, and provide independent in-
heritance mechanisms for each. In order to support binary
operations on objects within this framework, we introduce
“internal interfaces” which govern the way that function
components of one structure may access components of
another. The language design has been tested by writing a
number of program examples; an implementation is under
development in the context of a larger project.
1 Introduction
This paper describes a general module system which pro-
vides approximately the same functionality as a combina-
tion of Standard ML modules (signatures, structures and
functors) [HMM86], C
++
classes [Str86], and Ada packages
[US 80], enhanced with a form of specification. Our de-
sign effort stems from a larger project to produce a pro-
totyping language and programming environment. How-
ever, since our modules most closely resemble Standard
ML [Mac85, HMM86], we have chosen to present the mod-
ule design as an extension of that language. Many of the
subtyping ideas we use here have appeared in previous
Supported in part by an NSF PYI Award, matching funds from Digital
Equipment Corporation, the Powell Foundation, and Xerox Corporation;
NSF grant CCR-8814921 and the Wallace F. and Lucille M. Davis Faculty
Scholarship.
Supported by
the Defense Advanced Research Projects Agency/Information Systems
Technology Office under the Office of Naval Research contract N00014-
90-J1232 and by the Air Force Office of Scientific Research under Grant
AFOSR83-0255.
Supported by the Norwegian Research Council for Science and the
Humanities.
work, e.g., [CM89]. The primary new features of this lan-
guage are the generalization of structures to encompass
typed “first-class” objects, and inheritance primitives for
specifications and structures.
Our module design is a proper extension of Standard
ML. We extend signatures to specifications, which may con-
tain axioms as well as the types of structure components,
and extend the basic typing discipline to include subtyp-
ing. A new feature is a set of inheritance primitives for
specifications, and a parallel mechanism for structures. A
major change is that we essentially allow signatures (spec-
ifications) to be used as ordinary types. This has the con-
sequence of allowing structures to be passed as function
arguments, returned as function results, or stored in ref-
erence cells. In imprecise but suggestive jargon, we gen-
eralize Standard ML structures to first-class “objects.” To
keep the typing problem for this language tractable, we
must change our notion of equality for type components
of structures. In effect, we make use of the “first class ab-
stract data type” approach of [MP88], also used in the pro-
gramming language Quest [Car89]. However, this change
is applied in a systematic way which coincides with Stan-
dard ML typing for “top-level” structures and functors (the
cases allowed in Standard ML). Another change is that we
allow a specification name to appear in the type of one
of its components (a form of recursive type), and pro-
vide an expression self for defining such components
of structures. A novel feature, in comparison with other
object-oriented languages, is the use of internal interfaces
in specifications. The need for this new construct arises in
part from our separation of specification and implemen-
tation, and our flexible forms of inheritance; see Section
6.1. Finally, we allow parameterized specifications (which
seem especially useful for object-oriented programming)
and use bounded quantification [CW85, CCH
+
89, CM89]
rather than unconstrained ML polymorphism.
One of our language design goals is to support a range
of programming styles within a single framework. One
style of interest is the traditional program development
by “step-wise refinement,” using abstract data types and
specifications. However, it is recognized that for realis-
tic, large-scale programs, this idealized approach is not al-
ways effective. The main drawback is that in the pure style
advocated in the early 1970’s [Dij72, Par72], a significant
amount of effort may be put into the design before any
code is written. Therefore, program behavior cannot be
used to evaluate the program design until relatively late in
the development cycle. It is often better to write a simple
“prototype” early in the life of a project and use properties
of the prototype program as a basis for further refinement
of either the design or implementation. We therefore envi-
sion an approach to program development which involves
successive refinement of working code, with substantial
code reuse from one prototype to the next. Further dis-
cussion of our prototyping model may be found in [BL90].
We are unsatisfied with previous object-oriented lang-
uages for a number of reasons. One is the lack of support
for specified algebraic data types, which are familiar to
and valued by our intended user community. Another is
the way that specifications (typically signature informa-
tion only) and implementation are identified in a single
language construct. (An exception is Emerald [BHEL86],
which has several similar features, but lacks inheritance.)
We believe that the separation of specification from im-
plementation will prove beneficial to the development of
large object-oriented programs, both by allowing indepen-
dent use of either specification or implementation inher-
itance (see [Sny86]), and by allowing multiple implemen-
tations of a single “class” to coexist peacefully within a
single program. Thus we provide explicit language sup-
port for what is often achieved by the programming tech-
nique of so-called “abstract classes” [GR83, page 66-72].
In addition, our unified approach to abstract data types
and objects allows object-oriented concepts to be mixed
with other programming styles, and provides inheritance
of specification and implementation for algebraic data ty-
pes. Finally, we provide a compile-time typing discipline
which is more flexible than C
++
, without the type inse-
curity of Eiffel [Coo89]. Our type system is an extension
of ML polymorphism, based in part on the record calcu-
lus of [CM89], and including “F-bounded quantification”
[CCH
+
89] for specifying uniform behavior over function-
ally similar classes of objects. An additional goal of our
project, not reported here, is the integration of a form of
concurrency based on objects with concurrent methods.
An implementation is underway encompassing most of
the features discussed in this paper, as well as concur-
rency at the “method” level and various debugging tools.
In this paper, we will focus primarily on the program-
structuring aspects of our language, leaving further dis-
cussion of specifications and their use to later publica-
tions. The basis for our specification language and devel-
opment tools is [LvHKBO87, LHM
+
86]. To a large extent,
when restricted to the context of Standard ML, our speci-
fications do not differ substantially from Sanella and Tar-
lecki’s Extended ML [ST89]. Since we have not fully inves-
tigated the problem of ML-style type inference with sub-
typing, we will consider ML an explicitly typed language,
as explained in [MH88]. However, based on past experi-
ence, we do not foresee any serious obstacles in extend-
ing ML-style type inference to our language. The reader
concerned with type inference and subtyping may consult
[Mit84, Wan87, R
´
em89, JM88].
In Section 2, we give an informal introduction to the
module language by describing two simple stack exam-
ples. Section 3 follows with a brief glossary of basic ter-
minology. In Section 4, we briefly outline our use of sub-
typing. The main features of our form of modules are de-
scribed in Section 5, with motivating examples for certain
design decisions given in Section 6. Section 7 places our
extension of Standard ML in a type theoretic perspective.
2 Examples of abstract data types and classes
To give some feel for the module extension, we will com-
pare two forms of stack specification, one as an abstract
data type (ADT) and the other as a class of objects. While
these both use the same language constructs, the two pro-
gramming styles are distinctly different. In both exam-
ples, as an expository convenience, we use equality freely
without considering its logical interpretation.
In the traditional ADT approach, stacks might be intro-
duced by the following form of specification, which is our
extension of Standard ML signature:
specification Stack
spec =
external
type S[type t];
val empty[type t] : S[t];
fun push[type t] : t S[t] S[t];
fun pop[type t] : S[t] S[t];
fun top[type t] : S[t] t;
. . .
constraint
for all type t, x : t, s : S[t].
pop[t](push[t](x, s)) = s;
top[t](push[t](x, s)) = x;
. . .
end
Except for the explicit type parameters and axioms, this
is essentially the usual Standard ML signature. (The key-
word external indicates that the named components are
accessible outside the structure body; we also have inter-
nal interfaces, described later.) A module implementing
stacks would then give a representation of stacks (as a
function of the type of elements stored), and code for the
associated operations. Although our construct is slightly
different, we will use the Standard ML term structure for
a basic module.
structure Stack
impl : Stack spec =
struct
datatype S[type t] = empty
stk[t] |
stk of t S[t];
val empty[type t] = empty
stk[t];
fun push[type t](x : t, s : S[t]) : S[t] = stk(x, s);
. . .
end
For the purpose of writing Standard ML-style programs,
our most significant extensions are subtyping and inheri-
tance. Inheritance is useful for defining one module from
another, and subtyping allows the programmer to indicate
that elements of one type are substitutable for elements
of another. For example, we might specify that a type of
stacks with an additional operation is a subtype of the
stacks implemented as above. (A similar use of subtyp-
ing and ML abstract data types was presented in [JM88],
where a type inference algorithm is developed.) A perva-
sive difference between our module system and Standard
ML is our treatment of type equality, specifically, equal-
ity of type components of structures. We allow structures
to be used as first-class values, with the consequence that
within certain well-delineated contexts, type components
may become “hidden” as opposed to visible types. This is
discussed in more detail in Section 5.3 and from a type-
theoretic perspective in Section 7.
An object-oriented approach to stacks involves a “class”
of objects, each of which implements a single stack. We
will have one type of objects for each type of stack entry,
given by the following parameterized specification.
specification Stack
class[type t] =
external
fun is
empty : unit bool;
fun push : t Stack
class[t];
fun pop : unit Stack
class[t];
fun top : unit t;
. . .
constraint
for all type t, s : Stack
class[t], x : t.
(s.push(x)).top = x;
(s.push(x)).pop = s;
. . .
end
A structure M satisfying the specification Stack
class[t],
for some type t, is essentially an “object” with operations
M.push, M.pop, and so on. (The Standard ML type unit
is a trivial, one-element type used to indicate that M.pop,
for example, has an empty parameter list and returns a
value when called, possibly having a side effect.)
Objects are implemented as structures. Following Stan-
dard ML terminology, we refer to parameterized struc-
tures as functors. A general implementation of stack ob-
jects would be a functor which creates objects of type
Stack
class[t] when called with type parameter t.
functor Stack
object[type t] : Stack class[t] =
struct
val contents : ref List[t] = ref nil;
fun is
empty() : bool = (contents = 0);
fun push(x : t) : Stack
class[t] =
(contents : = cons(x, !contents)); self;
. . .
end
The list pointer contents is private to an object since it
does not appear in the Stack
class external interface.
For the reader unfamiliar with Standard ML, we note that
the operator ! returns the contents of an assignable refer-
ence cell. The body of push deserves explanation. This
is a sequence of expressions. The value returned is the
value of the second expression, self, after evaluating the
first for side effect. Since the value of self, which is not
part of Standard ML, is the entire structure, the effect of
s.push(x) is to add x to the contents list of s and re-
turn the resulting stack structure. Although without self
we could write a similar push function with only a side
effect, this would make compound expressions such as
s.push(x).top meaningless.
Since we separate the interface to an object from the
function which creates objects, it is straightforward to give
several implementations of a single class. This leads to
a difficulty with binary operations [Mit90a], which is re-
solved using internal interfaces, as discussed in Section
5.
3 Basic concepts and terminology
structure: Syntactically, a structure is an encapsulated set
of declarations, including types, functions (procedures)
and other values. Semantically, a structure is a com-
posite value determined by a set of such declara-
tions.
functor: A functor is a parameterized definition of a struc-
ture, as in Standard ML.
specification: A specification defines two interfaces, called
the external interface and the internal interface. Each
interface consists of a signature, giving names of
components and their types, and an optional set of
constraints (axioms) restricting the behavior of struc-
ture components. The external interface defines the
visible components of the specification, while the in-
ternal interface may impose additional demands on
all implementations of the specification. These are
described in greater detail in Sections 5.1 and 6.1. We
consider a specification a form of type, namely, the
type of all structures meeting this specification. Our
use of specification corresponds to Standard ML sig-
nature or Ada package specification, extended with
an internal interface and constraints.
type: A type is a collection defined by a type expression
or specification. Types are separated into two kinds,
which we call small types and large types. This dis-
tinction corresponds to the Standard ML distinction
between signatures and types. The small types are
basic types such as int, bool, string, types ob-
tained from these using constructors such as record
and array, and specifications with only abstract type
components. Values from any small type may be
stored in a reference cell, for example, or returned as
the result of a conditional expression. Specifications,
in general, define large types. Implicit coercion from
large to small types is discussed in Section 5.3. The
names small and large are related to constructive
type theory (see, e.g., [Mar84, C
+
86, Mac86, Mit90b]).
subtype: A type A is a subtype of B if every expression of
type A may be used in any context requiring an ex-
pression of type B, without type error. In fact not
all subtypes are recognized by the compiler, just as
most languages do not recognize all possible type
equalities. The compiler only recognizes A as a sub-
type of B if this follows from the form or declaration
of at least one of these types.
inheritance: We use “inheritance” informally to refer to
various mechanisms for using one declaration in writ-
ing another. We have separate inheritance mecha-
nisms for specifications and for structures.
4 Extending ML with subtypes
Subtyping is a general extension to Standard ML which ef-
fects both the so-called “core language” of basic expres-
sions and declarations, as well as the module system. Since
the subtyping ideas we use, outside of the module sys-
tem, have been proposed in previous papers [CW85, Mit84,
JM88] [CM89], we will only give a brief summary of sub-
typing. More detailed discussion of subtyping for speci-
fications and structures appears in Section 5. We use the
notation A <: B to indicate that A is a subtype of B.
There are five ways that a type A may become a subtype
of B, depending on the forms of A and B.
basic types: If A and B are basic, predefined types, then
A <: B iff this is given as part of the language defini-
tion.
type constructors: If A and B are types defined using the
same type constructor, then A <: B iff this follows
from the subtyping properties for this constructor.
For example, if A = A
1
× A
2
and B = B
1
× B
2
are both
cartesian products, then A <: B iff A
1
<: B
1
and A
2
<: B
2
.
specifications: If A and B are specifications, then A <: B
iff specification A includes all of the component dec-
larations and constraints of B, and the declaration
of either A or B makes it clear that subtyping is in-
tended.
declared type identifier: If the declaration of a type ide-
ntifier t specifies that t is a subtype of A, then t < : B
iff A <: B. A similar rule holds for formal parame-
ters: a formal parameter t is a subtype of B only if
this follows from a subtype or sharing constraint in
the formal parameter list containing t.
predefined supertypes: Several designated basic types are
supertypes of all types of a given form. For example,
record is a supertype of all record types.
Subtyping is essentially a generalization of type equal-
ity. In Standard ML, and most typed languages, we gener-
ally assume that if A and B are equal types, then any ex-
pression with one of these types also has the other. With
subtyping, we augment type equality with the so-called
“subsumption” rule:
If A <: B and expression e has type A,
then expression e has type B.
Subtyping may also be used in parameter lists, to restrict
the possible values of formal parameters. This is illus-
trated by example in Section 6.2.
A point worth mentioning briefly is the lack of connec-
tion between subtyping of specifications and subtyping of
abstract data types defined by these specifications. Con-
sider, for example, a specification Q
spec introducing a
type Q of queues with the usual operations, and a simi-
lar specification DQ
spec introducing a type DQ of double-
ended queues. Since any structure satisfying DQ
spec also
satisfies Q
spec, it is reasonable to consider DQ spec <: Q spec.
In common terms, this means that any implementation of
doubly-ended queues is also an implementation of queues.
However, if M : Q
spec is a structure satisfying Q spec and
DM : DQ
spec is a structure satisfying DQ spec, the compo-
nent type DM.DQ is not necessarily a subtype of M.Q.
5 General description of specifications and structures
5.1 Specifications
Syntactically, a specification has three main parts: an in-
heritance list, an external interface and an internal inter-
face. The inheritance list may give one or more specifi-
cations which may be extended, restricted or copied, as
described in Section 5.2 below. Each interface consists of
a signature, giving names of components and their types,
and an optional set of constraints restricting the behavior
of structure components. For the purpose of this paper,
the constraints may be regarded as axioms about the com-
ponents, written in a version of multi-sorted first-order
logic whose sorts are the types of the programming lan-
guage. The external interface describes the structure com-
ponents that are available outside the structure and the in-
ternal interface describes components that are visible only
to other instances of that specification and its subtypes.
The motivation for having separate internal and external
interfaces is illustrated in Section 6.1. When a type com-
ponent is listed as part of a specification, a list of subtypes
and supertypes of this type may be given.
5.2 Specification inheritance
There are three “inheritance” operations on specifications.
The first is extend. If specification A is defined by extend-
ing B, then the declaration of A need only mention com-
ponents and axioms that are not given in B. The result is
that specification A is equivalent to the union of the dec-
larations of A and B, and the type A becomes a subtype of
B. The converse operation is restrict. If A is defined by
restricting B, then the declaration of A lists components
and axioms of B that are to be omitted from A. The result
is that A is a supertype of B. A specification may extend
several specifications, defining a subtype of each.
The third operation is copy, which is essentially equiv-
alent to inserting a textual copy of a specification, and
does not result in a subtype or supertype. Either an entire
specification may be copied, or the two forms
copy hspecification
namei except . . .
copy hspecification
namei only . . .
may be used. In the first case, all components of a speci-
fication are copied, except those listed. The second form
only includes the listed components, which may be more
convenient if only a relatively small part of a specification
is required.
An added feature of extend and restrict is the abil-
ity to rename components. This adds some complexity to
subtyping for specifications, since the names of the com-
ponents in one type may not correspond directly to the
names of components in a supertype. However, the con-
version from one type to another is straightforward (and
easily implemented), and this feature seems to provide a
very effective way for the programmer to resolve name
conflicts in multiple inheritance. Renaming with copy is
straightforward since copy-ing a specification is not re-
quired to result in a subtype.
5.3 Structures
Our structures are similar to Standard ML structures, with
inheritance primitives added. The most significant differ-
ence is in our treatment of type equality.
The desire for decidable compile-time type checking
imposes certain restrictions on computations with ML mod-
ules ([MH88, HMM90]). For instance, ML signatures, struc-
tures and functors may only be declared at “top level,”
and functor application is restricted to top level. This is
because the type part of a functor application is required
at compile-time [MTH90, HMM90]. This conflicts with our
desire to support object-oriented programming, since we
would like to allow arbitrary computation on structures
such as the object-oriented style stacks mention in Sec-
tion 2.
Our solution to this problem is to allow arbitrary com-
putation with structures, but to limit type equality to what
may be determined at compile time. In a sense, where
Standard ML restricts the language, we choose instead to
restrict the degree of “dataflow analysis” used in type check-
ing. If a structure has no type components, then com-
puting type equality simply does not enter into the pic-
ture: A structure without type components is essentially
a record, and arbitrary computation may be allowed with-
out complication. Similarly, as shown in [MP88], if all type
components are abstract types, there is no need to restrict
computation on structures. The only problem arises when
a structure has a type component which we expect to be
equal to another type. In this case, we cannot expect to test
equality at compile time, if the structure is a result of an
arbitrary function call or conditional expression. Using a
systematic criterion which is related to the distinction be-
tween small and large types (see [Mac85, MH88, HMM90]),
we identify a class of compile-time evaluable structure ex-
pressions. These include the application of a top-level
functor to a top-level structure. If usage dictates that the
type component of a structure must be equal to another
type, then the type checker determines whether the struc-
ture is given by a compile-time expression. If so, then the
test for equality is based on the actual structure value. If
not, then the type is considered “abstract,” and distinct
from other types, and the test may therefore fail. This
mechanism is supplanted by the sharing constraints of
Standard ML, which we generalize to subtype assertions.
If a structure is a formal parameter, then the type relation-
ships guaranteed by sharing constraints in the parameter
list are acknowledged within the function body.
Although the above description of type equality has an
operational, algorithmic quality, the basic method has a
straightforward explanation using standard type-theoretic
concepts. This is summarized in Section 7. Essentially,
when a structure is used in a conditional expression, we
view this as an implicit coercion of visible type compo-
nents to abstract types.
Since structures have many similarities to records, it
might seem natural to identify objects with ML records
instead of structures. If ML typing were extended with a
flexible form of record typing, as in [Wan87, R
´
em89, CM89]
for example, this would allow much of the object-oriented
capability we provide. One important difference, however,
is inheritance. Rather than extend ML records with inheri-
tance, we have chosen to integrate objects into the module
system. This has the beneficial effect of allowing the same
primitives to be used for specification and implementation
of abstract data types, “objects,” and various hybrids.
5.4 Structure inheritance
One motivation for allowing separate inheritance of speci-
fication and implementation comes from the fact that these
two quite often work most naturally in “opposite direc-
tions” ([Sny86]). This may be illustrated by considering
queues and double-ended queues. It is natural to spec-
ify double-ended queues by extending the specification
of queues, since half of the specification of double-ended
queues is already present in the specification of queues.
On the other hand, if we have an implementation of double-
ended queues, then it is easy to implement queues by
simply hiding some operations. This implementation of
queues does not depend on how the implementation of
double-ended queues works, and so we could change the
latter without needing to redefine the former. However,
if we tried to implement double-ended queues by adding
operations to the implementation of queues, we lose this
abstraction. A change in the implementation of queues
might require us to change the implementation of double-
ended queues. In this case, separate inheritance allows us
to define both our specifications and implementations in
the most natural way.
There is one inheritance operation on structures, copy,
with several options. The options allow inheritance of any
or all components, renaming, and visibility restrictions.
The following examples, using both options, include all
but one of the basic keywords.
copy hspecification
namei only . . . rename . . .
hide . . .
copy hspecification
namei except . . . rename
. . . show . . .
In the first example, only the listed components are copied.
A list of renaming clauses, of the form hcomponent
namei to
hcomponent
namei, indicate which components are rena-
med, and how. All of the copied components become vis-
ible components of the new structure, except those listed
in the hide clause. The names except and show are com-
plements of only and hide.
The simplest semantic description is textual copy. To
give an example, an operation copy A only f, g in the
declaration of structure B indicates that B will have visible
components B.f and B.g derived from A. The behavior of
f and g is exactly the same as if both of their declarations
in A (if that is where they are declared) had been textu-
ally copied into the declaration of B. In particular, if f is a
function calling g, then this call in the body of f refers to
the inherited g. If f also calls a function h, then this call
will go to the function h declared in B, not A. This is es-
sentially the usual form of inheritance, as in Smalltalk, for
example, except that we do not require all of a structure
to be inherited. We are able to provide this flexibility as a
result of the fact that we do not force the two structures to
have related types (signatures or specifications). The more
complicated example copy A only f, g rename f to k
may be understood as a textual copy, but with f renamed
to k, including renaming all occurrences of f in the bodies
of f and g. The effect of hide is straightforward.
6 Motivating Examples
6.1 Internal interface
We present two examples that illustrate the use of internal
interfaces.
6.1.1 Time-stamped events
Consider time-stamped events. A specification for these
might look as follows:
specification time
stamped event =
external
fun precedes : time
stamped event bool;
fun event
name : unit string;
. . .
end
Our implicit intention is that each structure of type
time
stamped event will have a local real variable which
stores the time of the event. Since the only time-related
operation on time-stamped events is precedes, this vari-
able in not part of the external interface. However, if
ev1, ev2 : time
stamped event are two events, then
ev1.precedes(ev2) should compare the times of the two
events, returning a boolean value. This requires ev1 to
have access to the time of ev2. If the function precedes
must be implemented using only public operations on its
argument, then the only solution is to make the time of
an event public. This seems undesirable, since only other
time-stamped events need to see the time of an event.
The internal interface solves this problem appropri-
ately by letting us specify that every time-stamped event
must have a time component. By putting this require-
ment in the internal interface, we make the time compo-
nent of one event accessible to the function components
of other events, without making time generally available.
With an internal interface added, the specification will now
appear as follows:
specification time
stamped event( . . . ) =
external
fun precedes : time
stamped event bool;
fun event
name : unit string;
. . .
internal
val time : ref real;
end
6.1.2 Sets with union
Suppose we wish to specify and implement a class of set
objects with a union operation. In object-oriented style,
the union of two sets is computed by sending one set as
an argument to another. A first attempt at specifying set
objects might be as follows. For expository convenience,
we assume that equality on type elem is provided in some
way, and that an equation e1 = e2 has a meaningful inter-
pretation, even when evaluating one of these expressions
might ordinarily side-effect the other. We may explicitly
require equality on elem using means similar to those de-
scribed in Section 6.2.
specification SET[ type elem] =
external
fun empty : unit bool;
fun element : elem bool;
fun insert : elem SET[elem];
fun delete : elem SET[elem];
fun union : SET[elem] SET[elem];
fun intersect : SET[elem] SET[elem];
constraint
SET[elem].empty; (* all sets initially empty *)
for all o1, o2 : SET[elem], e1, e2 : elem.
o1.empty => not o1.element(e1);
o1.insert(e1).element(e2) <=>
e1 = e2 or o1.element(e2);
o1.delete(e1).element(e2) <=>
e1 e2 and o1.element(e2);
o1.union(o2).element(e1) <=>
o1.element(e1) or o2.element(e1);
o1.intersect(o2).element(e1) <=>
o1.element(e1) and o2.element(e1);
end SET;
If we try to implement the above specification, we run
into difficulties with union(s:SET[elem]). The problem
is that the natural, efficient implementations of union need
to iterate over the list of the elements in the argument s,
but there is no apparent way to do this using only the
public operations allowed on s. One not quite satisfac-
tory approach is to implement sets in a lazy style. While
this makes union easy to compute, it becomes difficult to
implement empty properly in the presence of intersection.
A more realistic approach is to extend the external inter-
face with a function element
list which returns a list of
all the elements in the set. This has the undesirable effect
of making more operations public than required. A more
subtle reason for keeping element
list out of the exter-
nal interface is that we expect equality to be a congruence
relation, but we do not require equal sets to have the same
ordered element list.
We may solve this problem using internal interfaces.
An internal interface requiring an element
list function
forces each set to define a function element
list. How-
ever, this function is not publicly visible it may only be
used by other set objects. The following example illus-
trates the use of constraints to state correctness proper-
ties of internal interface functions.
internal
fun element
list : unit List[elem];
constraint
local in
list : elem List[elem] bool
for all s : SET[elem], l : List[elem], e : elem.
in
list(e, l) <=> e = hd(l) or in list(e, tl(l));
s.element(e) <=> in
list(e, s.element list())
end
The declaration local in
list . . . indicates that the
predicate identifier in
list is local to the constraint, and
not part of the interface. With this internal interface, im-
plementing SET is straightforward.
We should emphasize the fact that the internal inter-
face makes it easier for a single program to contain several
different implementations of the same specification. In
the pure object-oriented style, any operation in a specifi-
cation that needs an argument of the same type (binary op-
erations, in essence) cannot make any assumptions about
the argument, since it could have an arbitrary internal rep-
resentation. The internal interface makes it easier to write
binary operations by allowing the specification to impose
restrictions in addition to the external interface.
Internal interfaces are used in a variant of the C
++
friend concept. If elements of type A need access to the
internal interface of B structures then the specification of
B may list A as a friend type.
6.2 Polymorphism over structurally similar types of ob-
jects
In many situations, it is useful to define structures or func-
tions which work uniformly over all types of objects with
certain operations. Although it is a simple example, sort-
ing illustrates the main points. Using abstract data types, a
polymorphic sorting function would require a comparison
(order) relation on the type of list elements to be sorted.
This form of sort function would have type
sort[type t] : (t t bool) List[t] List[t]
In an object-oriented style, we would expect each object
in the input list to have a comparison function compo-
nent. This requirement must be incorporated into the
type of sort, as a restriction on the type of list elements.
Based on previous investigation [CCH
+
89], the appropri-
ate way to restrict the type of list element appears to be
through the introduction of a parameterized specification
ordered[type t]. A parameterized specification ordered[type
t] is essentially a function which, given any type t, returns
a specification requiring each x : t to have a comparison
function x.less
than of type x.less than : t bool. In-
tuitively, the function x.less
than tells, for any y : t, whether
x is less than y. Given this parameterized specification, we
can write the type of sort as
sort[ type t <: ordered[t] ] : List[t] List[t]
Intuitively, if t is a type of objects, each having a com-
parison function component, then sort maps lists of t’s
to lists of t’s. Although the reader might see the restric-
tion type t <: ordered[t] on the type of list elements as
“recursive,” it is not; see [CCH
+
89] for further discussion.
Note that the same form of parameterization would be
required if we wish to parameterize a binary search tree
specification or implementation by the type of ordered
records inserted into trees.
If specifications of objects are simply signatures, then
the straightforward form of qualified polymorphism de-
scribed above is sufficient. However, since we wish to give
constraints in addition to signature information, a prob-
lem arises. This may be seen by trying to define the pa-
rameterized specification ordered[type t]. A naive first
attempt might be the following.
specification ordered[type t] =
external
fun less
than : t bool
constraint
for all x, y, z : t.
not x.less
than(x)
x.less
than(y) and y.less than(z) implies
x.less
than(z)
end
Although this specification might at first appear to ex-
press the intended restriction on t, it is not syntactically
well-formed. The problem is that the constraint section
assumes that if x : t, then x has a function component
x.less
than. However, there is no assumption about the
type t which justifies this. The inquisitive reader may wish
to ponder this point, and attempt to devise a solution be-
fore reading further.
One solution to this problem is to use two specifica-
tions, one giving the signature part of this specification,
and the other (depending on the first) expressing the con-
straint. More specifically, we may write the following two
specifications.
specification order
sig[type t] =
external
fun less
than : t bool
end
specification
order
constraint[type t < : order sig[t] ] =
constraint
for all x, y, z : t.
not x.less
than(x)
x.less
than(y) and y.less than(z) implies
x.less
than(z)
end
The important change from the single specification is that
the parameter t of the order
constraint is explicitly re-
quired to be a subtype of order
sig[t], and so it is clear
within the specification body that any x : t has a less
than
component of the appropriate type.
Since the parameterized specification we originally de-
sired is a kind of “sequential conjunction” of two specifica-
tions, we may define ordered by the following declaration.
specification ordered[type t] = order
sig[t] andthen
order
constraint[t]
We also provide syntax for defining ordered and sim-
ilar parameterized specifications directly.
7 Type-theoretic perspective
Our extension of Standard ML may be understood within
the type-theoretic framework used in [Mac86, MH88, HMM90]
to explain the Standard ML module system. While the in-
dexed categorical view of [HMM90] provides some inter-
esting insight into the distinction between compile-time
and run-time, we will restrict ourselves to the simpler and
more accessible framework of [MH88], based on a predica-
tive typed lambda calculus XML with two “universes” of ty-
pes (see also [Set89]). In this section, we will briefly review
XML and then describe an extension XML
+
with subtyping
and constructs illustrating the essential features of our ex-
tended module system. Since the basic subtyping features
of XML
+
are subsumed by the more ambitious system of
[CM89], we emphasize the modules as opposed to subtyp-
ing in this document.
The language XML is a typed lambda calculus with two
universes of types, U
1
and U
2
, which we have referred to
informally as the “small” and “large” types, respectively.
Since the language may be defined with respect to any
choice of basic types, type constructors and expression
constants, the small types of XML may include basic ty-
pes such as integers and booleans, and type constructors
such as list. The expressions of these types may be poly-
morphic, depending on type variables, or may depend on
structures.
The modules of XML are constructs of the second uni-
verse (U
2
), which contains general products and sums over
the small types, and U
1
itself. For those unfamiliar with
basic type theory, we review the notion of general sums
and general products. If A is a collection (such as a type)
and B(x) is an expression defining a collection for each
x A, then the general product type Πx : A. B is the col-
lection (or type) of functions f from A to the union
xA
B
of all B(x) such that for each x A, we have f (x) B(x).
For example, if list(t) is the type of t lists, for any small
type t : U
1
, and nil(t) is the empty list of type list(t), then
we can say that the function nil has type Πt : U
1
. list (t).
For collection A and A-indexed family of collections B(x)
as above, the general sum type Σx : A. B is the collection
(or type) of pairs ha, bi such that a : A and b : B(a). For
example, Σt : U
2
. list (t) is the type of pairs ht
`
, `i with list
` belonging to type list(t`).
As explained in [Mac86, MH88], the signatures, struc-
tures and functors may be regarded as syntactic sugar
for general products and sums, and their members. More
specifically, the signature of a Standard ML structure may
be regarded as a general sum type, and a structure itself as
an element of such a type, since a structure is essentially
a kind of tuple. Although functor signatures are omitted
from Standard ML, the type of a Standard ML functor is
a general product type Πx : A. B, with type A giving the
type of the functor parameter and type B defining the type
of the resulting structure. The reason a functor does not
have an ordinary function type is that the type of structure
produced may depend on the actual structure parameter,
as in the following example:
functor M(S : spec type t; val x : t end) :
spec
type s; val x : list[S.t]
end
In our extension of Standard ML modules, we have both
general sum types, as specifications of structures, and
general product type specifications for functors. In addi-
tion, we have parameterized specifications, which actually
have types of the form U
2
U
2
. . . U
2
, technically
taking us beyond U
2
.
One novel feature of our module system, in compari-
son with Standard ML, is an impredicative treatment of ex-
istential types and a related implicit coercion from U
2
to
U
1
. As described in [MP88], see also [CW85], existentially
quantified types correspond to the programming language
notion of abstract types. Like Quest [Car89], our existen-
tial types are impredicative, so that a specification with
only abstract (or, in Ada terminology, “private”) types may
be considered a U
1
type instead of a U
2
type.
For example, the following Standard ML specification
specification S =
spec
type t;
val x : t
end
would ordinarily be regarded as a general sum type
S : : = Σt : U
1
. t
Since this type is in U
2
and not U
1
, a function such as
conditional could not be applied to structures of this type.
More precisely, conditional is an operation with type (in
curried form)
cond : Πt : U
1
. bool t t t.
The reason for restricting conditional to U
1
types is that a
higher level conditional would interfere with compile-time
type checking by allowing, for example, type expressions
such as cond (e > 5) int bool, for arbitrary integer expres-
sion e. Since conditional only applies to U
1
arguments, we
cannot apply cond to a structure M : S.
However, if the type component of some structure M :S
is hidden, then there is no problem with using a condi-
tional expression. Some examples of this are discussed
in [MP88], including an example choosing between sparse
and dense matrix implementations at run time. In order to
achieve this flexibility, we recognize that if the type com-
ponent of M : S is to be considered abstract, then we may
think of S as the existential (as opposed to general sum)
type
S
: : = t : U
1
. t
belonging to U
1
instead of U
2
. In effect, the typechecker
recognizes that there is a canonical map
hide : Σt : U
1
. t t : U
1
. t
which does nothing but “hide” the identity of the type com-
ponent of a structure by mapping a pair into a type which
does not have a first projection function. By automatically
coercing structures from U
2
to U
1
, we allow structures to
be treated as first-class values, essentially losing only the
ability to test equality between type components.
8 Conclusion
We have developed an extension of Standard ML modules
which incorporates subtyping, inheritance and a form of
“object” in a uniform way. The language design has been
tested by writing a number of examples, and an implemen-
tation is under development. While the language design
does not solve all of the problems discussed in [Mit90a],
for example, the current design seems practically useful,
and more flexible in certain respects than existing typed
object-oriented languages. In particular, the separation of
specification from implementation and the use of internal
interfaces to allow efficient, type-safe implementation of
binary operations seem useful. We have found “F-bounded
polymorphism,” introduced in [CCH
+
89], to be a useful
primitive, and sufficiently expressive for describing fami-
lies of classes satisfying certain axiomatic specifications.
A direction we are currently exploring is an extension of
objects (Standard ML structures) with concurrent meth-
ods. In particular, the design and use of an appropriate
specification language seem to require some considera-
tion.
Acknowledgements: This module design is part of the de-
sign of a prototyping language and system, under devel-
opment in collaboration with a team lead by David Luck-
ham (Stanford), Frank Belz (TRW), Sigurd Meldal and John
Mitchell. We are grateful to members of this team for dis-
cussion, guidance and feedback. We also thank Dinesh
Katiyar for a number of helpful discussions and sugges-
tions.
References
[BHEL86] A. Black, N. Hutchinson, E.Jul, and H. Levy.
Object structure in the Emerald system. In
Proc. ACM Symp. on Object-Oriented Pro-
gramming: Systems, Languages, and Appli-
cations, pages 78–86, October 1986.
[BL90] F. Belz and D.C. Luckham. A new approach
to prototyping ada-based hardware/software
systems. In Proc. ACM Tri-Ada’90 Confer-
ence, December 1990. To appear.
[C
+
86] R.L. Constable et al. Implementing Mathemat-
ics with the Nuprl Proof Development System,
volume 37 of Graduate Texts in Mathematics.
Prentice-Hall, 1986.
[Car89] L. Cardelli. Typeful programming. Techni-
cal Report 45, DEC Systems Research Center,
1989. presented at IFIP Advanced Seminar
on Formal Descriptions of Programming Con-
cepts.
[CCH
+
89] P. Canning, W. Cook, W. Hill, J. Mitchell,
and W. Olthoff. F-bounded quantification for
object-oriented programming. In Functional
Prog. and Computer Architecture, pages 273–
280, 1989.
[CM89] L. Cardelli and J.C. Mitchell. Operations on
records. In Math. Foundations of Prog. Lang.
Semantics, 1989. To appear. Also available as
DEC SRC Technical Report 48, August 1989,
60 pages.
[Coo89] W.R. Cook. A proposal for making Eiffel type-
safe. In European Conf. on Object-Oriented
Programming, pages 57–72, 1989.
[CW85] L. Cardelli and P. Wegner. On understanding
types, data abstraction, and polymorphism.
Computing Surveys, 17(4):471–522, 1985.
[Dij72] E.W. Dijkstra. Notes on structured pro-
gramming. In O.J. Dahl, E.W. Dijkstra, and
C.A.R. Hoare, editors, Structured Program-
ming. Academic Press, 1972.
[GR83] A. Goldberg and D. Robson. Smalltalk–80:
The language and its implementation. Addi-
son Wesley, 1983.
[HMM86] R. Harper, D.B. MacQueen, and R. Milner.
Standard ML. Technical Report ECS–LFCS–
86–2, Lab. for Foundations of Computer Sci-
ence, University of Edinburgh, March 1986.
[HMM90] R. Harper, J.C. Mitchell, and E. Moggi. Higher-
order modules and the phase distinction. In
Proc. 17-th ACM Symp. on Principles of Pro-
gramming Languages, pages 341–354, Jan-
uary 1990.
[JM88] L. Jategaonkar and J.C. Mitchell. ML with ex-
tended pattern matching and subtypes. In
Proc. ACM Symp. Lisp and Functional Pro-
gramming Languages, pages 198–212, July
1988.
[LHM
+
86] D. C. Luckham, D. P. Helmbold, S. Meldal, D. L.
Bryan, and M. A. Haberler. Task sequenc-
ing language for specifying distributed ada
systems. In Lecture Notes in Computer Sci-
ence, Number 275, pages 249–305. Springer-
Verlag, May 1986.
[LvHKBO87] David Luckham, Friedrich W. von Henke,
Bernd Krieg-Br¨uckner, and Olaf Owe. Anna
- A Language for Annotating Ada Programs.
In Lecture Notes in Computer Science, Num-
ber 260. Springer-Verlag, July 1987.
[Mac85] D.B. MacQueen. Modules for Standard ML.
Polymorphism, 2(2), 1985. 35 pages. An
earlier version appeared in Proc. 1984 ACM
Symp. on Lisp and Functional Programming.
[Mac86] D.B. MacQueen. Using dependent types to ex-
press modular structure. In Proc. 13-th ACM
Symp. on Principles of Programming Langu-
ages, pages 277–286, 1986.
[Mar84] P. Martin-L
¨
of. Intuitionistic Type Theory. Bib-
liopolis, Napoli, 1984.
[MH88] J.C. Mitchell and R. Harper. The essence of
ML. In Proc. 15-th ACM Symp. on Principles of
Programming Languages, pages 28–46, Jan-
uary 1988.
[Mit84] J.C. Mitchell. Coercion and type inference
(summary). In Proc. 11-th ACM Symp. on
Principles of Programming Languages, pages
175–185, January 1984.
[Mit90a] J.C. Mitchell. Toward a typed foundation for
method specialization and inheritance. In
Proc. 17-th ACM Symp. on Principles of Pro-
gramming Languages, pages 109–124, Jan-
uary 1990.
[Mit90b] J.C. Mitchell. Type systems for programming
languages. In J. van Leeuwen, editor, Hand-
book of Theoretical Computer Science. North-
Holland, 1990. (To appear.).
[MP88] J.C. Mitchell and G.D. Plotkin. Abstract ty-
pes have existential types. ACM Trans.
on Programming Languages and Systems,
10(3):470–502, 1988. Preliminary version ap-
peared in Proc. 12-th ACM Symp. on Principles
of Programming Languages, 1985.
[MTH90] Robin Milner, Mads Tofte, and Robert Harper.
The Definition of Standard ML. MIT Press,
1990.
[Par72] D. Parnas. On the criteria to be used in de-
composing systems into modules. Communi-
cations of the ACM, 5(12):1053–1058, Decem-
ber 1972.
[R
´
em89] D. R
´
emy. Typechecking records and vari-
ants in a natural extension of ML. In 16-th
ACM Symposium on Principles of Program-
ming Languages, pages 60–76, 1989.
[Set89] R. Sethi. Programming Languages: Concepts
and Constructs. Addison-Wesley, 1989.
[Sny86] A. Snyder. Encapsulation and inheritance in
object-oriented programming languages. In
Proc. 1-st ACM Symp. on Object-Oriented Pro-
gramming Systems, Languages, and Applica-
tions, pages 38–46, October 1986.
[ST89] D. Sanella and A. Tarlecki. Towards formal
development of ML programs: foundations
and methodology. Technical Report ECS-
LFCS-89-71, Laboratory for Foundations of
Computer Science, February 1989.
[Str86] B. Stroustrop. The C
++
Programming Lan-
guage. Addison-Wesley, 1986.
[US 80] US Dept. of Defense. Reference Manual for
the Ada Programming Language. GPO 008-
000-00354-8, 1980.
[Wan87] M. Wand. Complete type inference for simple
objects. In Proc. 2-nd IEEE Symp. on Logic in
Computer Science, pages 37–44, 1987. Corri-
gendum in Proc. 3-rd IEEE Symp. on Logic in
Computer Science, page 132, 1988.
... Recent work by Mitchell and his co-workers [96] ...
Article
Full-text available
It has been amply demonstrated in recent years that careful attention to the structure of systems software can lead to greater flexibility, reliability, and ease of implementation, without incurring an undue penalty in performance. It is our contention that advanced programming languages- particularly languages with a mathematically rigorous semantics, and featuring higher-order functions, polymorphic types, first-class continuations, and a useful and powerful module system-are ideally suited to expressing such structure. Indeed, our previous research has shown that the use of an advanced programming language can have a fundamental effect on system design, leading naturally to system architectures that are highly modular, efficient, and allow re-use of code. We are thus working to demonstrate the viability and benefits of advanced languages for programming real-world systems. To achieve this, we have organized our research into the three areas of language design, compiler technology, and systems building. This report describes the current plans for this effort, which we refer to as the Fox project.
... small or big hash-tables, ...). Various approaches have been proposed ( [MMM91], [MP88], [DCH03], [Rus98]...) to solve this problem. But, as far as the author knows, they come with no proof of soundness. ...
Article
Full-text available
We present a programming language and its type system containing ML-like modules with abstract datatypes as first-class values, and a proof of type soundness. The result is based on a translation into an extension of Raffalli's system ST, a type sys-tem with subtyping that separates computational and logical parts of proofs. The great expressive power of ST, together with its realizability seman-tics, allow us to give an interpretation of abstract datatypes in terms of union, and to express the meaning of the "dot notation" by using the axiom of choice.
... Recent work by Leroy 22] provides a more type-theoretic treatment of the same concepts but, once again, relies on non-trivial extensions of the underlying dependent type theory. Additional machinery is necessary to extend the SML module system beyond its current deenition, for example, to support modules as rst-class values 31, 20], higher-order modules 12, 34, 41, 25, 7], polymorphism 19], and transluscent sums or manifest types 11, 21]. This last item is motivated in part by the desire to admit a form of separate compilation; this is not possible with the current deenition of SML 2]. ...
Article
We describe extensions of the Hindley-Milner type system to support higher-order poly-morphism and rst-class structures with polymorphic components. The combination of these features results in àcore language' that rivals the expressiveness of the Standard ML module system in some respects and exceeds it in others.
Article
We present a new, syntax-directed framework for Curry-style type systems with subtyping. It supports a rich set of features, and allows for a reasonably simple theory and implementation. The system we consider has sum and product types, universal and existential quantifiers, and inductive and coinductive types. The latter two may carry size invariants that can be used to establish the termination of recursive programs. For example, the termination of quicksort can be derived by showing that partitioning a list does not increase its size. The system deals with complex programs involving mixed induction and coinduction, or even mixed polymorphism and (co-)induction. One of the key ideas is to separate the notion of size from recursion. We do not check the termination of programs directly, but rather show that their (circular) typing proofs are well-founded. Termination is then obtained using a standard (semantic) normalisation proof. To demonstrate the practicality of the system, we provide an implementation accepting all the examples discussed in the article.
Article
Programming wireless embedded networks is challenging due to severe limitations on processing speed, memory, and bandwidth. Staged programming can help bridge the gap between high level code refinement techniques and efficient device level programs by allowing a first stage program to specialize device level code. Here we introduce a two stage programming system for wireless sensor networks. The first stage program is written in our extended dialect of Scala, called Scalaness, where components written in our type safe dialect of nesC, called nesT, are composed and specialized. Scalaness programs can dynamically construct TinyOS-compliant nesT device images that can be deployed to motes. A key result, called cross-stage type safety, shows that successful static type checking of a Scalaness program means no type errors will arise either during programmatic composition and specialization of WSN code, or later on the WSN itself. Scalaness has been implemented through direct modification of the Scala compiler. Implementation of a staged public-key cryptography calculation shows the sensor memory footprint can be significantly reduced by staging.
Conference Paper
Programming wireless embedded networks is challenging due to severe limitations on processing speed, memory, and bandwidth. Staged programming can help bridge the gap between high level code refinement techniques and efficient device level programs by allowing a first stage program to specialize device level code. Here we introduce a two stage programming system for wireless sensor networks. The first stage program is written in our extended dialect of Scala, called Scalaness, where components written in our type safe dialect of nesC, called nesT, are composed and specialized. Scalaness programs can dynamically construct TinyOS-compliant nesT device images that can be deployed to motes. A key result, called cross-stage type safety, shows that successful static type checking of a Scalaness program means no type errors will arise either during programmatic composition and specialization of WSN code, or later on the WSN itself. Scalaness has been implemented through direct modification of the Scala compiler. Implementation of a staged public-key cryptography calculation shows the sensor memory footprint can be significantly reduced by staging.
Article
System F ≤ is an extension of second-order typed lambda calculus, where a subtype hierarchy among types is defined, and bounded second-order lambda abstraction is allowed. This language is a basis for much of the current research on integration of typed functional languages with subtypes and inheritance. An algorithm to perform type checking for F ≤ expressions has been known since the language Fun was defined. The algorithm has been proved complete, by the author and P.-L. Curien, which means that it is a semi-decision procedure for the type-checking problem. In this paper we show that this algorithm is not a decision procedure, by exhibiting a term which makes it diverge. This result was the basis of Pierce's proof of undecidabilit y of typing for F ≤ . We study the behavior of the algorithm to show that our diverging judgement is in some sense contained in any judgement which makes the algorithm diverge . On the basis of this result, and of other results in the paper, we claim that the chances that the algorithm will loop while type-checking a "real program" are negligible. Hence, the undecidability of F ≤ type-checking should not be considered as a reason to prevent the adoption of F ≤ as a basis for defining programming languages of practical interest. Finally, we show the undecidability of an important subsystem of F≤.
Article
Central features of object-oriented programming are method inheritance and data abstraction attained through hierarchical organization of classes. Recent studies show that method inheritance can be nicely supported by ML style type inference when extended to labeled records. This is based on the fact that a function that selects a field ƒ of a record can be given a polymorphic type that enables it to be applied to any record which contains a field ƒ. Several type systems also provide data abstraction through abstract type declarations. However, these two features have not yet been properly integrated in a statically checked polymorphic type system. This paper proposes a static type system that achieves this integration in an ML-like polymorphic language by adding a class construct that allows the programmer to build a hierarchy of classes connected by multiple inheritance declarations. Moreover, classes can be parameterized by types allowing “generic” definitions. The type correctness of class declarations is statically checked by the type system. The type system also infers a principal scheme for any type correct program containing methods and objects defined in classes.
Article
Emerald is an object-based language for the construction of distributed applications. The principal features of Emerald include a uniform object model appropriate for programming both private local objects and shared remote objects, and a type system that permits multiple user-defined and compiler-defined implementations. Emerald objects are fully mobile and can move from node to node within the network, even during an invocation. This paper discusses the structure, programming, and implementation of Emerald objects, and Emerald's use of abstract types.
Chapter
We define a simple collection of operations for creating and manipulating record structures, where records are intended as finite associations of values to labels. We study a second-order type system over these operations, supporting both subtyping and polymorphism. Our approach unifies and extends previous notions of records, bounded quantification, record extension, and parametrization by row-variables. The general aim is to provide foundations for concepts found in object-oriented languages, within the framework of typed lambda-calculus.