A Theory of Objects by Abdali
A Theory of Objects by Abdali
Editors
David Gries
Fred B. Schneider
Castillo, Gutierrez, and Hadi, Expert Systems and Probabilistic Network Models
Martin Abadi Luca Cardelli
A Theory of Objects
, Springer
Martin Abadi Luca Cardelli
Luca Cardelli (current address):
Systems Research Center Microsoft Research
Digital Equipment Corporation Cambridge, CB2 3NH
Palo Alto, CA 94301 UK
USA
Series Editors:
David Gries Fred B. Schneider
Department of Computer Science Department of Computer Science
Cornell University Cornell University
Upson Hali Upson Hali
Ithaca, NY 14853-7501 Ithaca, NY 14853-7501
USA USA
With ni ne illustrations.
The use 01 general descriptive names, trade names, trademarks, etc., in this publication,
even il the lormer are not especially identilied, is not to be taken as a sign that such names,
as understood by the Trade Marks and Merchandise Marks Act, may accordingly be used
Ireely by anyone.
design, and programming texts. The diagrams given next should help in selecting the
relevant subsets.
Concept Map
The first diagram shows the general organization of the book. Each row represents a
chapter, and each column represents a theme. The black stripes indicate the occurrence
of themes in chapters. A scan of a row indicates the major themes developed in a chap-
ter. A scan of a column indicates the flow of a theme through the chapters.
.1 .-
3 Advanced Class-Based Features
4 I. Object-Based Languages
•• rrr .=
5 Modeling Object-Oriented Languages
Part I 6 Untyped Calculi
• rr rl
7 First-Order Calculi
8
•
Subtyping
•I. • · I.
9 Recursion
10 Untyped Imperative Calculi
I.'. I. •
11 First-Order Imperative Calculi
12 I. A First-Order Language
I.
Part II 13 Second-Order Calculi
14 I. I.
•
A Semantics
r-
r- ,. • •
15 f--
Definable Covariant Self Types
16
.1 • • 1. •
Primitive Covariant Self Types
17 Imperative Calculi with Self Types
18
•• I•• I. ••
Interpretations of Object Calculi
19 A Second-Order Language
Part III 20
• •
A Higher-Order Calculus
21 A Language with Matching
PREFACE vii
Chapter Dependencies
The next diagram shows the dependencies between chapters: each chapter should be
read after reading the chapters above it in the graph. Some sections of Chapters 12, 19,
and 21 are singled out as depending only on the Review and on each other; those sec-
tions assume'some familiarity with the notation used elsewhere in the book.
Review
1-5
Part I
12.1-4
/
12.5
Part II
19.1-4
/
19.5
Part III
20 _______ ;1.1-4
21.5
viii A THEORY OF OBJECfS
Acknowledgments
Many people have helped us in the preparation of this book.
We are grateful to Ramesh Viswanathan, with whom we wrote a paper that is the
origin of Chapter 18. We are also grateful to Kim Bruce, Adriana Compagnoni, Bill Kal-
sow, John Lamping, Paul-Andre Mellies, John Mitchell, Gordon Plotkin, and Scott
Smith for fruitful discussions.
In addition, we received many valuable comments on drafts of this book from
Felice Cardone, Antony Courtney, Dave Detlefs, Luis Dominguez, Kathleen Fisher,
Andy Gordon, Jason Hickey, Brian Howard, Michael Isard, Ole Madsen, Jens Palsberg,
Gareth Rees, Scott Smith, Valery Trifonov, David Ungar, and Peter Wegner.
Finally, we thank our colleagues at Digital's Systems Research Center and our
manager, Bob Taylor, for their support.
Pointers
We expect to maintain a Web page for general information, sample sections, and aux-
iliary material about this book. At the time of writing, this page is located at:
<http : //www.research . digital.com/SRC/personal/
Luca_Cardelli/TheoryOfObjects . html>
Springer servers are located at:
<http://www.springer-ny . com> (North America)
<http://www . springer.de> (Europe)
EPILOGUE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
CONTENTS xiii
classes to more primitive notions. Our object calculi are loosely modeled after object-
based languages, but are even more primitive.
After the review, the main body of the book is divided into three parts where we
discuss semantic, typing, and programming structures of increasing sophistication. At
the end of each of the three parts, we present an object-oriented programming lan-
guage that embodies the main features considered in that part. Given some familiarity
with our notation, these languages should be understandable independently of the
more formal material. For each language, we give an interpretation into one of our
object calculi, establishing the correctness of the language's type system.
In Part I of the book we begin the development of our theory by introducing a tiny
untyped object calculus. This is a calculus where the only entities are objects and object
operations, with a built-in notion of self. In a sense, this calculus embodies the Small-
talk credo that anything can be modeled as an object. We go even further than Small-
talk by showing that objects can represent functions and numbers, and that classes can
be programmed from objects. Several important variants of classes arise naturally.
After studying the untyped object calculus, we associate various type structures
with it. We consider, in particular, object types, recursive types, subtyping, and
dynamic types. These basic type constructions give us a type system rich enough to
interpret a first object-oriented language. This language is, in rough terms, similar to
languages such as Simula and Modula-3. Although lacking much of the convenience of
common languages, it is remarkably expressive in that it integrates class-based and
object-based notions.
In Parts II and III we move beyond basic type constructions and we investigate the
Self type (the type of self). This topic brings us into the realm of polymorphism, data
abstraction, and type operators. We review standard constructions that type theory
provides to model these concepts, and incorporate them into our object calculi. We also
give typing rules for the Self type and prove their correctness, in the context of both cal-
culi and languages.
The Self type often occurs only as the type of the results of methods. We show that,
in this special case, it can be understood through a combination of data abstraction and
type recursion, and that method inheritance requires polymorphism. The general case
where the Self type may occur in argument positions is also important; it arises in the
presence of binary methods, which take arguments of the type of self. The program-
ming and typing constructions related to binary methods are still in flux. Nonetheless,
we show how to analyze them using fairly standard constructions from type theory.
We conclude Parts II and III with two object-oriented languages that incorporate the
Self type, and we discuss their subtle rules.
At the beginning of this Prologue we remarked on a mismatch between objects and
functions. In the course of the book we develop a number of ideas and techniques that
enable us to assess that mismatch more precisely. Specifically, we consider the problem
of translating typed object calculi into typed function calculi. This is, in a sense, the
problem of programming in object-oriented style within a typed procedural language.
We show that this is possible in principle, given a sufficiently expressive procedural
PROLOGUE 3
language, but we argue that this would be too inconvenient in practice. Therefore we
find that the expressiveness of object-oriented languages cannot be emulated easily by
procedural languages.
· ·· ·· ·· · 0
type operators
·
4 A THEORY OF OBJECTS
. .. · · · . · · ·
13.2 13.2 13.2 13.2
functions
function types
·· · ·· • ·· · ··
sub typing
·
recursive types
side-effects
· · . · ·
quantified types
· · · ·
Several combinations of function and object calculi are defined in the book but are
not represented in these tables.
REVIEW
Object-Oriented Features
1 OBJECT ORIENTATION
1.1 Objects
The object-oriented approach to programming is based on an intuitive correspondence
between a software simulation of a physical system and the physical system itself. An
analogy is drawn between building an algorithmic model of a physical system from
software components and building a mechanical model of a physical system from con-
crete objects. By analogy, the software components are themselves called objects. In its
purest form, the object-oriented approach recommends that every system be devel-
oped according to this analogy.
The development of a mechanical model includes analysis, design, and implemen-
tation aspects. Consider, for example, the task of building a mechanical model of the
solar system. The analysis aspect consists in realizing that planets move along precise
orbits. The design aspect consists in inventing a mechanism for moving spheres along
such orbits. The implementation aspect consists in preparing and assembling spheres,
gears, and springs.
All three aspects of constructing a model involve entities we may call objects, but
with different connotations. In analysis, planets are seen as objects, but orbits clearly
are not. In design, the intangible orbits are modeled by concrete objects such as orbital
tracks and gears. Entirely new objects, such as power sources, may appear. In imple-
mentation, a spring may be an object that realizes the power source abstraction.
Object-oriented programming similarly involves analysis, design, and implemen-
tation aspects, with concrete objects replaced by software objects. The term object in
programming refers to a component of a software model, not to a component of the
system being modeled. The proper analogy is between software objects and objects in
a "real model" that one may imagine building, rather than objects in the "real world" .
For example, orbital tracks may be represented as software objects.
This approach to programming originated with Simula, which was initially dedi-
cated to solving simulation (model building) problems [57). Its main concepts were fur-
ther refined in Small talk, where the concreteness of objects was originally intended as
a pedagogical tool. Since then, the object-oriented methodology has been exploited in
1.2 Reuse
It is often claimed that object-oriented languages allow reuse of software components
better than traditional procedural languages. In support of this claim, we survey
abstractly the reuse mechanisms that are provided exclusively by object-oriented lan-
guages, and the main implications of these mechanisms. A more detailed discussion is
carried out in the following chapters.
1. OBJECT ORIENTATION 9
A software component is reusable when it can be easily used in more than one con-
text. For example, a module can be reused by importing it in several other modules,
and a generic module can be reused by instantiating it with different parameters. In all
cases, reuse entails the replacement of a component (or a placeholder) with another
component in a context. In traditional procedural languages, such a replacement
requires an exact agreement in type or interface.
In object-oriented languages there are two distinctive kinds of replacements.
Objects can be replaced by other objects, and methods can be replaced by other meth-
ods. These forms of replacement do not require exact agreement of type or interface,
only approximate agreement.
Various mechanisms allow replacing objects. In general terms, one may replace an
object with a new one that has at least the same set of attributes. Any additional
attributes of the new object remain as invisible attributes; they are preserved but are not
directly accessible.
The replacement of methods is called overriding, while the reuse of existing meth-
ods is called inheritance. New objects, or new generators of objects, are derived from old
ones by a mixture of reuse (inheritance) and variation (overriding). When a method is
overridden by a new one, the new method must conform to the interface of the old one,
but with some flexibility. In particular, the new method may be more specific than the
old one, in interface or in behavior, and may use attributes that are available only in the
derived object.
An almost inevitable consequence of these replacement mechanisms is the notion
of self. By the special name self, a method can refer to its host object, and hence to its
sibling methods (the other methods of the same object). Through inheritance and over-
riding, the siblings of a method may change. Therefore self has a dynamic meaning: it
always gives access to the current siblings of a method. This dynamic notion of self is
crucial, because it allows a method to exhibit a new behavior when inherited into a
derived object, depending on the siblings it finds there. Without a notion of self, the
behavior of methods is more rigid and method reuse is limited.
A further consequence of these replacement mechanisms is that methods are
closely bound to objects. Because of the flexibility in object and method replacement,
one cannot know statically the precise kind of all the objects that may be dynamically
bound to a variable, and whether those objects have invisible attributes. Moreover, one
cannot know statically what methods an object possesses, and whether those methods
rely upon invisible attributes of their host object. Extracting a method from an object,
and reusing it in a context that does not provide the appropriate invisible attributes,
would be unsound. Thus, unlike procedures operating on data structures, methods are
integral and inseparable parts of objects.
parameter in the contents field and returns nothing. Within these methods, the special
identifier self refers to the host object. The fields and methods of an object are collec-
tively called its attributes.
The intended behavior of objects can be understood in terms of the naive storage
model of Figure 2-1. In that model, an object is internally represented as a reference to
a record of attributes. An operation on an object attribute implicitly bypasses the refer-
ence in order to access the attribute. Program variables hold references, not attribute
records. Parameter passing and assignment copy references, not the associated
attribute records. Therefore parameter passing and assignment produce sharing of
attribute records.2
An object can be created from a class c by the construction new c. More precisely,
new c allocates an attribute record and returns a reference to it. The attribute record
contains the initial values and the method code specified by c. Distinct executions of
new c produce distinct objects; that is, they produce references to distinct attribute
records. An object generated from class c via new is colloquially called "an object of
class c" or "an instance of class c".
We indicate by InstanceTypeOj{c) the type of objects of class c. In the following
example, a new cell is bound to the variable myCeli with type InstanceTypeOj{cell):
var myCell: InstanceTypeOj{cell) := new cell;
By introducing the type InstanceTypeOj{cell), we have made an important distinction
between classes and types. We could have chosen to consider cell, instead of lnstance-
TypeOj{cell), as the type of the objects generated from cell. However, such an identifica-
tion of classes with types would cause confusion later.
Given a cell named myCell, we can extract the value of the contents field by
myCell.contents, and we can update it by myCell.contents:=n. 3 The contents fields of dis-
tinct objects of class cell change independently upon updates. That is, each object of
class cell has a separate contents field .
2 Some languages, such as C++ and Oberon, expose the distinction between attribute
records and references to attribute records in order to distinguish between stack-
allocated and heap-allocated objects. Other languages, such as Simula, Small talk,
and Modula-3, hide this distinction and deal exclusively with heap-allocated
objects.
2. CLASS-BASED LANGUAGES 13
Given a cell, we can also invoke its methods. For example, myCell.set(3) is a method
invocation that runs the code for set with the formal parameter n bound to 3 and with
self bound to myCell. As a further example of method invocation, we define a proce-
dure named double whose body doubles the contents of a cell:
procedure double(aCell: InstanceTypeOftcell) is
aCell.set(2 * aCell.get());
end;
. .. method suite
. ..
Figure 2-2. Method suites.
3 In some languages a field can be declared private (accessible only to the methods of
its class) or protected (accessible only to the methods of its class and subclasses).
Although representation hiding is important, it is not unique to object-oriented
programming. Even within object-oriented programming, protection of fields and
methods can be obtained by other mechanisms such as lexical hiding or subtyping
(see Section 8.5.2). We do not enforce representation hiding for fields.
14 REVIEW. OBJECT-ORIENTED FEATURES
7 simula and C+ + allow overriding only for those methods that have been declared
overridable (Virtual) in the superclass. We implicitly consider all methods to be vir-
tual, as in Small talk and Modula-3.
16 REVIEW. OBJECT-ORIENTED FEATURES
inherits from a class c, self refers to an object of the subclass c', not to an object of the
original class c. In particular, through self, one can access methods redefined in c', and
cannot access the original methods from c.
The special identifier super in a new method can be used to invoke the old version
of a method from a superclass. More precisely, super.m(a) stands for the invocation of
the method m of the current direct superclass, with argument a, and with self still
bound to the current self. The latter condition is critical; it ensures that any occurrence
of self within the method m is interpreted with respect to the current subclass, and not
with respect to any superclass. B
The implementation of method lookup must now be reconsidered in light of our
discussion of subclasses. In the delegation-based implementation of inheritance there
is a method suite for each (sub )class. In the case of dynamically typed languages, these
suites are searched, from subclasses to superclasses, until an appropriate method is
found (Figure 2-3). In the case of statically typed languages (where the hierarchy of
aReCell. ~ ~
method suites is known at compile-time) the suites are collapsed at each level so that
iterative search is not necessary; in a sense, an embedding model is used after the first
step of delegation (Figure 2-4).
Multiple inheritance is obtained when a class inherits attributes from more than one
other class. (Otherwise one has single inheritance.) There would be nothing peculiar
about multiple inheritance, except that it is usually specified by declaring a class as
having multiple superclasses. Then one must decide what to do about conflicts and
duplications among the attributes of the superclasses. The simplest solution is to iden-
tify all duplications of identical attributes, and to forbid any conflicts; many other solu-
tions have been adopted. With multiple superclasses, there is no unique path upwards
for method lookup to follow, and hence the meaning of super becomes rather delicate. 9
9 See, for example, the mechanisms for multiple inheritance adopted in Eiffel, C++,
and CLOS.
18 REVIEW. OBJEcr -ORIENTED FEATURES
10 Some languages, including Simula, C++, and Modula-3 allow both kinds of dis-
patch. The static versions are based on distinct syntactic constructions.
2 . CLASS-BASED LANGUAGES 19
II Languages that support stack-based objects have to deal with such truncation
effects on assignment and parameter passing. In these cases, either subsumption is
forbidden (C++) or coercion is a series of field assignments (Oberon).
12 C++ adopts this purist position, but uniquely on grounds of efficiency, and then
resorts to unsafe casts for recovering type information [115) . A recent addition to
C++ introduces a mechanism for examining the run-time type of objects.
20 REVIEW. OBJECT-ORIENTED FEATURES
13 Instances of typecase appear in Modula-3 (as narrow and type case), in Simula (as
qua and inspect), and in Oberon (as type guards).
2. CLASS-BASED LANGUAGES 21
We examine function types next. The type A~B is the type of functions with argu-
ment type A and result type B.
We say that ~ is a contravariant operator in its left argument because A~B varies
in the opposite sense as A; the right argument is instead covariant:
A~B <: A'~B' provided that A' <: A and B <: B'
14 Eiffel still favors covariance of method arguments; the issue is di scussed in [55) and
in (89), Chapter 22. In Beta, unsound behavior is caught by run-time checks. Covari-
ance can be soundly adopted for multiple dispatch, but using a different set of type
operators (48).
2. CLASS-BASED LANGUAGES 23
This argument leads to the notion of Self types. The keyword Self represents the
type of self. Instead of assigning the result type InstanceTypeOfic) to m, we now write:
class c is
var x: Integer := 0;
method mO: Self is .. . self ... end;
end;
The typing of the code of m relies on the assumptions that Self is a subtype of
InstanceTypeOfic), and that self has type Self. Field updates to self preserve its Self
type. For soundness, the result of m must be shown to have type Self, and not just type
InstanceTypeOfic).
When c' is declared as a subclass of c, the result type of m is still taken to be Self.
However, Self is then regarded as a subtype of InstanceTypeOfic'). Thus Self, as the
result type of a method, is automatically specialized on subclassing.
There are no drawbacks to extending classical class-based languages with Self in
covariant positions, for example as the result type of methods. This extension increases
expressive power and prevents loss of type information at no cost other than properly
keeping track of the type of self. We can even allow Self as the type of fields; this is
sound as long as those fields are updated only with self or updated versions of self.
A natural next step is to allow Self in contravariant (argument) positions. This is
what Eiffel set out to do (88). Unfortunately, contravariant uses of Self are unsound for
subsumption as can be demonstrated by simple counterexamples [55) . As we have seen,
types in argument positions of inherited methods may be generalized, not specialized.
The proper handling of Self in contravariant positions is a major new develop-
ment in class-based languages, and goes well beyond their classical features. We dis-
cuss it in Sections 3.4 and 3.5.
3 ADVANCED CLASS-BASED FEATURES
One of the main characteristics of classical class-based languages is the strict correla-
tion between inheritance, subclassing, and subtyping. A great economy of concepts
and syntax is achieved by identifying these three relations. The identification also con-
fers much flexibility in the use of subsumption: an object of a subclass, some of whose
methods may have been inherited, can always be used in place of an object of a super-
class by virtue of subtyping.
There are situations, however, in which inheritance, subclassing, and subtyping
conflict. Opportunities for code reuse, both by inheritance and by parameterization,
turn out to be limited by the coincidence of these relations. Therefore considerable
attention has been devoted to separating them. The separation of subclassing from sub-
typing is becoming commonplace; other separations are more tentative. In this chapter
we examine various possible distinctions.
It seems natural to take such a protocol as the type of instances of cell. Recall the classes
cell and reCell:
class cell is
var contents: Integer := 0;
method getO: Integer is return self.contents end;
method set(n: Integer) is self.contents := n end;
end;
subclass reCell of cell is
var backup: Integer := 0;
override set(n: Integer) is
self. backup := self.contents;
super.set(n);
end;
method restoreO is self.contents := self.backup end;
end;
We introduce two object types Cell and ReCell that correspond to these classes. We
write them as independent types, but we could introduce syntax to avoid repeating
common components.
ObjectType Cell is
var contents: Integer;
method getO: Integer;
method set(n: Integer);
end;
ObjectType ReCell is
var contents: Integer;
var backup: Integer;
method getO: Integer;
method set(n: Integer);
method restoreO;
end;
These types list attributes and their types, but not their implementations. They are suit-
able to appear in interfaces, and to be implemented separately and in more than one
way.
We use ObjectTypeOJ(cell) as a meta-notation for the object type Cell. This type can
be mechanically extracted from class cell. Therefore we may write either 0 : ObjectTy-
peOJ(cell) or 0: Cell. The main property we expect of ObjectTypeOfis that:
new c : ObjectTypeOJ(c) for any class c
3. ADVANCED CLASS-BASED FEATURES 27
Different classes cell and ce1l1 may happen to produce the same object type Cell,
equal to both ObjectTypeOf(cell) and ObjectTypeOf(ce1l1). Therefore objects having type
Cell are required only to satisfy a certain protocol, independently of attribute imple-
mentation.
Property (4) is a reformulation for ObjectTypeOJ of property (3) of Section 2.4, the
subclassing-is-subtyping property. While property (3) is a double implication, the con-
verse of (4) does not hold: there may be unrelated c and c' such that ObjectTypeOJ(c) =
o and ObjectTypeOj(c') = 0', with 0' <: o.
Therefore we have partially decoupled subclassing from subtyping by relaxing the
double implication (3) to the single implication (4). Subclassing still implies subtyping,
so all the previous uses of subsumption are still allowed. But, since subsumption is
based on sub typing and not subclassing, we now have even more freedom in sub-
sumption.
In conclusion, the notion of subclassing-is-subtyping can be weakened to subclass-
ing-implies-subtyping without loss of expressiveness, and with a gain in separation
between interfaces and implementations.
var lunch: F;
method eat(food: F);
end;
ObjectType Vegetarian is
type F <: Vegetables;
var lunch: F;
method eat(Jood: F);
end;
The meaning of the type component F <: Food in Person is that, given a person, we know
that it can eat some Food, but we do not know exactly of what kind. The lunch attribute
provides some food that a person can eat.
We can build an object of type Person by choosing a specific subtype of Food, for
example F = Dessert, picking a dessert for the lunch field, and implementing a method
with parameter of type Dessert. We have that the resulting object is a Person, by forget-
ting the specific F that we chose for its implementation.
Now the inclusion Vegetarian <: Person holds. A vegetarian subsumed into Person
can be safely fed the lunch it carries with it, because the vegetarian was constructed
with F <: Vegetables. A limitation of this approach is that a person can be fed only the
food it carries with it as a component of type F, and not food obtained independently.
The second relationship expresses a bit more directly the fact that there exists a subpro-
tocol relation, and that this is in fact a relation between operators, not between types.
Whenever we have some property common to several types, we may think of
parameterizing over these types. So we may adopt one of the following forms of
parameterization:
ObjectOperator P1[X <: MaxProtocol[X]] is .. . end;
ObjectOperator P2[P -<: MaxProtocol] is .. . end;
Then we can instantiate PI to PI [MinMax], and P2 to P2 [MinMaxProtocol].
These two forms of parameterization seem to be equally expressive in practice. The
first one is called F-bounded parameterization [35, 38, 75]. The second form is higher-order
bounded parameterization, defined via pointwise subtyping of type operators; we treat it
formally in Part III. See [4] for a comparison between these two forms of parameteriza-
tion.
Instead of working with type operators, a programming language supporting sub-
protocols may conveniently define a matching relation (denoted by <#) directly over
types. 17 The properties of the matching relation are designed to correspond to the def-
inition of subprotocol. Depending on the choice of subprotocol relation, we have:
5 <# T if 5 <: T-Protocol[S] (F-bounded interpretation)
or
5 <# T if S-Protocol -<: T-Protocol (higher-order interpretation)
With either definition we have MinMax <# Max.
Matching does not enjoy a subsumption property (that is,S <# T and s : 5do not
imply that s :T); however, matching is useful for parameterizing over all the types that
match a given one:
ObjectOperator P3[X <# Max] is .. . end;
The instantiation P3[MinMax] is legal.
In summary, even in the presence of contravariant occurrences of Self, and in
absence of subtyping, there can be inheritance of binary methods like max. Unfortu-
nately, subsumption is lost in this context, and quantification over subtypes is no
longer very useful. These disadvantages are partially compensated by the existence of
a sub protocol relation, and by the ability to parameterize with respect to this relation.
17 The concept of matching and its name are taken from PolyTOIL; Theta and School
have similar notions.
4 OBJECT-BASED LANGUAGES
The main features of class-based languages appeared fully formed in Simula. Object-
based languages, in contrast, have emerged more gradually. Their main principles
were first gathered in the Treaty of Orlando (114); to a large extent, even their most basic
notions are still evolving (118).
Object-based languages are intended to be both simpler and more flexible than tra-
ditional class-based languages. Many object-based languages originated in the Lisp,
Smalltalk, and artificial intelligence communities, where extreme flexibility is highly
valued [11,31,32,78,80]. As a consequence, little attention has been devoted to designing
typed object-based languages, except for simple ones such as Emerald, and for recent
ones such as Cecil and Omega. In this chapter we review the main features of object-
based languages, considering issues of inheritance and typing.
prototype acts much like a class for the objects cloned from it, and much like a super-
class for the prototypes cloned from it. Unlike a class, however, a prototype is a fully
functional object in its own right. Is
Cloning would not be of much use without some way of mutating (customizing)
the clones so that they can differ from their prototypes. In the simplest case, we may
customize a field:
cellClone.contents := 3;
More generally, we may want to customize the behavior of a clone by modifying its
methods. The dynamic modification of the behavior of individual objects is a unique
property of object-based languages. We could write:
cellClone.get :=
method 0: Integer is
if self.contents < 0 then return 0 else return self.contents end;
end;
replacing the method get of a particular object with a method that never returns nega-
tive numbers.
This method update operation can be seen as an elementary example of a rather
rich collection of operations that are used for mutating clones [27]. With respect to
those, method update has the advantage of being simple and statically typable, as we
shall see in great detail. I9
As a further illustration of method update, we reformulate the reCell example from
Section 2.3 in object-based style:
ObjectType ReCell is
var contents: Integer;
method getO: Integer;
method set(n: Integer);
method restoreO;
end;
object reCell: ReCell is
var contents: Integer := 0;
method getO: Integer is return self.contents end;
20 Omega and Kevo support object extension, but essentially as an environment oper-
ation that applies to all existing objects of a certain category. Sometimes the con-
struction of extended prototypes is considered external to the language and
relegated to the user interface [26, 32).
4. OBJECf-BASED LANGUAGES 39
four categories. The attributes of a donor may be obtained implicitly or explicitly and,
orthogonally, those attributes may be either embedded into a host or delegated to a donor.
In implicit inheritance, one or more objects are designated as the donors, and their
attributes are implicitly inherited. In explicit inheritance, individual attributes of one
or more donors are explicitly designated. An intermediate possibility is to explicitly
designate a named collection of attributes that, however, does not form a whole object:
this is mixin inheritance [33]. We generally consider only implicit and explicit inherit-
ance as the extreme points of a spectrum.
More important is the distinction between embedding and delegation inheritance.
With embedding, the attributes inherited from a donor become part of the host (in prin-
ciple, at least; various optimizations can be applied) [32, 117, 118]. With delegation, the
inherited attributes remain part of the donor, and are accessed via an indirection from
the host.
In both embedding and delegation the meaning of self is the usual one: it is the
original receiver of an invocation. The semantic differences between the two forms of
inheritance concern only how changes to a donor object affect the host objects that
inherit its attributes. In embedding, host objects are independent of their donors. In
delegation, complex webs of dependencies may be created.
4.4 Embedding
One possible interpretation of inheritance is that host objects contain copies of the
attributes of donor objects. We call this the embedding interpretation of inheritance, or
simply embedding (Figure 4-1). The embedding interpretation is appealing because it
provides the simplest explanation of the standard semantics of self as the receiver of
an invocation. The invocation of an inherited method, under the embedding interpre-
tation, works exactly like the invocation of an original method.
aCell. ~
contents 0
){et (code for get)
set (code for set)
aReCell. .. contents 0
backup 0
){et (new code for ){et)
set (new code for set)
restore (code for restore)
Embedding was described as part of one of the first proposals for prototype-based
languages (31) . Only recently, though, it has been adopted by languages such as Kevo
and Obliq. We call these languages embedding-based.
As we discussed in the previous section, inheritance can be specified explicitly or
implicitly. Explicit forms of embedding inheritance can be understood as reassembling
parts of old objects into new objects. Implicit forms of embedding inheritance can be
understood as ways of extending copies of existing objects with new attributes.
In the explicit version of embedding, we designate methods and fields to be copied
from other specific objects. For instance, we might write our restorable cell example as
follows:
object cell: Cell is
var contents: Integer := 0;
method getO: Integer is return self.contents end;
method set(n: Integer) is self.contents := n end;
end;
object reCellExp: ReCell is
var contents: Integer := cell.contents;
var backup: Integer := 0;
method getO: Integer is
return embed cell.getO;
end;
method set(n: Integer) is
self.backup := self.contents;
embed cell.set(n);
end;
method restoreO is self.contents := self.backup end;
end;
We write embed o.m( .. . )to embed the method m of object 0 into the current object; this
embedding construction is used to replace both the implicit inheritance of the get
method and an occurrence of super.set(n) in the set method. The meaning of embed
cell.set(n) is to execute the set method of cell with self bound to the current self, and not
with self bound to cell as in a normal invocation cell.set(n). Moreover, the code of set is
embedded in reCellExp, so it is not affected by updates to the cell object.
The code for the get method is rather verbose, given that its only purpose is to redi-
rect an invocation. In practice, we could adopt an abbreviation such as:
method get copied from cell;
However, we still need the general embed construct for the set method.
To avoid typing problems, we need to assume that the object that follows embed
is statically known (the object itself, not just its type). In this sense, the situation is anal-
4. OBJECT-BASED LANGUAGES 41
ogous to that in a subclass definition, where we assume that the superclass is statically
known.
In the implicit version of embedding, which we discuss next, we may designate a
particular object as the donor of methods and fields to be copied into a new object. For
instance, we may write the restorable cell example as follows:
object cell: Cell is
var contents: Integer := 0;
method getO: Integer is return self.contents end;
method set(n: Integer) is self.contents := n end;
end;
object reCellImp: ReCell extends cell is
var backup: Integer := 0;
override set(n: Integer) is
self. backup := self.contents;
embed cell.set(n);
end;
method restoreO is self. contents := self.backup end;
end;
These definitions are not much different from the corresponding class definition of Sec-
tions 2.1 and 2.3. The extends declaration designates the donor object cell for reCellImp;
we assume that the object that follows extends is statically known. As a consequence
of this declaration, reCellImp is an object containing a copy of the attributes of cell, with
independent state. The effect of override is to replace a method of the donor in the host;
note that the embed construct is still needed.
Since objects are independent of their donors, the definitions of both reCellImp and
reCellExp can be seen as convenient abbreviations for a stand-alone definition:
object reCell: ReCell is
var contents: Integer := 0;
var backup: Integer := 0;
method getO: Integer is return self.contents end;
method set(n: Integer) is
self.backup := self.contents;
self. contents := n;
end;
method restoreO is self.contents := self.backup end;
end;
Alternatively, we could define an equivalent object by a pure extension of cell followed
by a method update.
42 REVIEW. OBJECT-ORIENTED FEATURES
4.5 Delegation
Prototype-based languages that permit the sharing of attributes across objects are
called delegation-based. In operational terms, delegation is the redirection of field
access and method invocation from an object or prototype to another, in such a way
that an object can be seen as an extension of another.
In the most common forms of implicit delegation inheritance (49, 121), a child object
(the host) designates another object as its parent (the donor). In implementation terms,
the child contains a parent link (a reference to its parent) through which the attributes
of the parent can be obtained (Figure 4-2). When an attribute is not found in an object,
its parent is searched.
aCell. •
contents
• 0
I ....
aReCell. •
contents 0
backup 0
set (new code for set)
restore (code for restore)
receiver, reCellImp, and not to the parent, cell. Hence the result of getO is, as desired, the
integer stored in the contents field of reCellImp.
As with embedding, we assume that, when a child is constructed by delegation, its
parent is statically known. This restriction, adopted by statically-typed delegation-
based languages such as Cecil, is important for checking the compatibility of the child
with the parent. Knowing the type of the parent is not sufficient for soundness. For
example, if the contents field of cell is first forgotten by subsumption, and a child is
defined with a contents field having type Boolean, say, then the inherited get method
returns a Boolean when invoked via the child because of the interpretation of self as the
original receiver. This result is wrong because the declared result type of the get
method is Integer.
More generally, a parent may be an unknown object as long as its true type is stat-
ically known, somehow. Alternatively, it must be statically known that the parent does
not have certain attributes. These options can be handled in sophisticated type systems
with negative information (43, 61, 70, 126). For simplicity, we assume that parent objects
are statically known.
We turn briefly to explicit delegation inheritance, where an object delegates indi-
vidual methods explicitly to other objects (SO) and where there are no parent declara-
tions. 21 We may write:
object reCellExp: ReCell is
var contents: Integer := cell.contents;
var backup: Integer := 0;
method getO: Integer is return delegate cell.getO end;
method set(n: Integer) is
self.backup := self.contents;
delegate cell.set(n);
end;
method restoreO is self.contents := self.backup end;
end;
Explicit delegation provides a clean way of delegating operations to multiple
objects. Multiple delegation is sometimes allowed in the implicit delegation model, by
listing multiple parents. As in the case of implicit multiple inheritance, this is problem-
atic because it introduces ambiguities in attribute lookup. Multiple parents can be more
cleanly represented in terms of explicit delegation. 22
21 Ellie has both explicit embedding and explicit delegation. Self is primarily based on
implicit delegation, but also supports a primitive for explicit delegation.
22 A mechanism similar to explicit multiple delegation can be found in Ellie. How-
ever, this mechanism does not preserve self as the original receiver of an invoca-
tion, and therefore it is redirection rather than delegation.
4 . OBJEcr-BASED LANGUAGES 45
contents
• 1
~
contents
• 2
~et (code for ~et) ~et (other code for ~et)
set (code for set) set (other code for set)
parent link
aReCelle ~
contents
- 0
backup 0
set (new code for set)
restore (code for restore)
25 The terminology is from Self. Cecil has the similar notions of abstract, template, and
concrete objects, respectively. Since Cecil is statically typed, the distinction is better
enforced there than in Self. Omega also has a sharp distinction between prototypes
and clones.
48 REvIEW. OBJECT-ORIENTED FEATURES
In Self and Cecil, traits and prototypes together are used to emulate classes. Traits
typically contain methods. Prototypes typically contain fields and have traits for par-
ents. Normal objects are cloned from prototypes, and thus share the traits of their pro-
totypes (Figure 4-4). Several traits may be used by a single prototype in a form of
multiple inheritance.
prototype trait
aCell. ~
object
clone(aCell). ~
In the spirit of classless languages, traits and prototypes are still ordinary objects.
However, there is a separation of roles. Traits are intended only as the shared parents
of normal objects; they should not be used directly or cloned. Prototypes are intended
only as object (and prototype) generators via cloning; they should not be used directly
or modified. Normal objects are intended only to be used and to carry local state; they
should rely on traits for their methods. These distinctions may be purely methodolog-
ical, or may be enforced; for example, some operations on traits and prototypes may be
forbidden to protect these objects from accidental damage.
The separation of roles between traits and prototypes, although natural and useful
in a delegation context, violates the spirit of the prototyping approach [59] . Traits nor-
mally contain only part of the intended attributes of normal objects. In particular,
methods in traits refer to fields contained in prototypes. Traits, being themselves
incomplete, cannot function on their own. This is a departure from the original proto-
typing idea that every object is a self-contained exemplar of behavior.26
Therefore it is hard to regard traits as ordinary objects, even when they live in a
universe where "everything is an object". Traits cannot be used as normal objects and,
in typed languages, they need to be typed specially in the context of their associated
prototypes. In this respect, traits have many of the properties of classes.
With the separation between traits and other objects, we seem to have come full
circle back to class-based languages and to the separation between classes and
instances. In fact, the traits-prototypes partition in delegation-based languages looks
exactly like an implementation technique for classes. A similar traits-prototypes parti-
tion in embedding-based languages corresponds to a different implementation tech-
26 Omega enforces a distinction between prototypes and clones, but remains faithful
to the idea of prototypes as complete objects.
4. OBJECT-BASED LANGUAGES 49
nique for classes that trades space for access speed. Cecil and Omega come even closer
to class-based languages by abandoning dynamic inheritance.
Thus class-based notions and techniques are not totally banned in object-based
languages. Rather, they naturally resurface as programming conventions that may be
cast into language constructs. The design space between object-based and class-based
languages should be seen as a continuum [114]. The achievement of object-based lan-
guages is to make clear that classes are just one of the possible ways of generating
objects with common properties. Objects are more primitive than classes and should be
understood and explained before classes.
them in different ways. The reconstruction is not always as comfortable as the original.
Still, this decomposition provides an explanation for class-based mechanisms and sug-
gests possible variations. In our technical development we decompose object-based
languages further into object calculi. The emulation of original object-based or class-
based features may not be straightforward and convenient. The raw expressive power
is, however, the same. By reduction to a few powerful primitives we gain a clearer
semantics and we simplify the task of reasoning formally .
28 The language Self unifies field update with method invocation; clients perform
only method invocations. But in objects there is still a distinction between so-called
data slots and assignment slots.
5. MODELING OBJECT-ORIENTED LANGUAGES 53
guages, not yet found in class-based ones. Its potentially unpredictable effects have led
to the search for better-behaved, restricted, dynamic inheritance mechanisms [119].
Method update is one of these better-behaved mechanisms. It is statically typable, and
can be used to emulate mode-switching. With method update we avoid the amorphous
and dangerous aspects of dynamic inheritance [59, 119] while maintaining its dynamic
specialization aspects originally advocated by the Treaty of Orlando [114].
In this chapter we develop an untyped calculus of objects, while keeping in mind some
future typing requirements. We view objects as primitive and define a direct semantics
for them. In order to illustrate the expressiveness of the calculus, we develop some
small but challenging examples and show how to represent classes and inheritance.
The object calculus does not include functions, but we show how to write functions
and fix point operators in terms of objects. Conversely, we describe attempts to encode
our object primitives in untyped A-calculi.
Notation
• [ ... , I=b, ... j stands for [ ... / 1=C;(y)b, ... j, for an unused y. Here I=b is afield .
• o.I:=b stands for o . I~C;(y)b, for an unused y. Here o.I:=b is afield update.
A method invocation o.Ij reduces to the result of the substitution of the host object
for the self parameter in the body of the method named Ij • Note that the semantics of
invocation is defined abstractly by substitution and not, for example, by function appli-
cation.
A method update o.lj~C;(y)b reduces to a copy of the host object where the updated
method has been replaced by the updating one.
6.2.1 Syntax
The following grammar describes our pure untyped object calculus. A <;-term is an
expression generated by this grammar.
Syntax of the <;-calculus
a,b ::= terms
x variable
[Ii=<;(xi)bi iE1.. n l object formation (Ii distinct)
a.1 field selection / method invocation
a.I*<;(x)b field update / method update
Notation
• The notation a,b ::= . .. inductively describes a set of expressions, where a and b
range over the expressions being defined. The preceding definition means: a
term a or b is a variable x, or an expression of the form [Ii=<;(xi)b i iE1.. n l (where the
Ii are labels, the Xi are variables, and the bi are terms), and so on. Parentheses can
be added at will to group subexpressions.
6. UNTYPED CALCULI 61
• Here and in the sequel, a construct binding a variable in a scope has the form
~(x)b; other examples are A(x)b, A(x:A)b, and 'v'(X)B. In syntactically ambiguous
situations, the body of such a construct extends to the right as much as possible,
for example up to a closing parenthesis. For example, A(x)b(x) means A(x)(b(x»
and not (A(x)b )(x).
To complete the formal syntax of the ~-caIculus we give the definitions of free vari-
ables (FV) and substitution (b{n-a») for ~-terms.
Object scoping
FV(~(y)b) ~ FV(b)-(y)
FV(x) ~ (x)
FV([li=~(xi)bi iE!..nJ) ~ viE!..n FV(~(Xi)bi)
FV(a.l) ~ FV(a)
FV(a.l~~(y)b ) ~ FV(a) v FV(~(y)b)
Object substitution
(~(y)b){xf-e» ~ ~(y')(b«yf-Y'&«Xf-c») for y'~FV(~(y)b)vFV(c)v(x)
X{Xf-C' ~ C
yixf-cJ ~ Y fory;t x
[li=~(xi)bi iE!..n]«Xf-c) ~ [li=(~(xi)bi)«xf-c» iE!..n]
(a.l){xf-e» ~ (a{xf-c}).l
(a.l~~(y)b)«xf-e» ~ (aRxf-c» ). l~«~(y)b )(Xf-c&)
Notation
• A closed term is a term without free variables.
• We write b(x) to highlight that x may occur free in b.
• We write bie», instead of bRxf-c». when b(x) is present in the same context.
• We identify ~(x)b with ~(y)(b«xf-y»), for any y not occurring free in b. (For exam-
ple, we view ~(x)x and ~(y)y as the same method.)
• We identify any two objects that differ only in the order of their components.
(For example, we view [ll=~(Xl)h, 12=~(x2)b2] and [12=~(x2)bz, 11=~(Xl)bd as the
same object.)
6.2.2 Reduction
In Definition 6.2-1 we capture the primitive semantics of objects given in Section 6.1.2.
This definition sets out three reduction relations: top-level one-step reduction (>-+),
one-step reduction (--), and general many-step reduction (--). As is customary, we do
not make error conditions -explicit. We simply assume that objects and methods are
62 PART I. UNTYPED AND FIRST-ORDER CALCULI
used consistently, and that otherwise computations stop without producing a result
(so, for example, [1 .1does not reduce to a result).
Definition 6.2-1 (Reduction relations)
(1) We write a >+ b if for some 0 == [IF~(xi)b;{xi} ie!."] and jE Ln, either:
a == o./j and b == bjgol, or
a == o./j~~(x)c and b == [lj=~(x)c, IF~(xi)bi ie(l..n}-UI].
(2) A context C[-] is a term with a single hole, and C[d] represents the result of filling
the hole with the term d (possibly capturing free variables of d).
We write a - b if a == C[a' ], b == C[b'], and a' >+ b', where C[-] is any context.
(3) We write -- for the reflexive and transitive closure of - .
o
For example,
let 0 == [1=~(x)[]].1
and C[-] - [k=~(xH
hence C[o] == [k=~(x)o]
then:
o >+ [I
C[o] - [k=~(x)(]]
C[o].k -- (]
The reduction relation satisfies a Church-Rosser property:
Theorem 6.2-2 (Church-Rosser)
If a -- b and a -- c, then there exists d such that b -- d and c -- d.
o
The proof of this result follows the method ofTait and Martin-Lof. The sequence of def-
initions and lemmas is standard (see (24), pp. 59-62).
6.2.3 Equations
We can derive an untyped equational theory from the untyped reduction rules. Our
purpose in defining this theory is capturing a notion of equality for ~-terms . We may
use this notion of equality when we want to say that two objects behave in the same
way. To this end, we reformulate the reduction rules as equational rules, and we add
rules for symmetry, transitivity, and congruence; the congruence rules guarantee that
equals can be substituted for equals.
In the following table we give the rules of the equational theory. Each rule has a
number of premise judgments above a horizontal line and a single conclusion judg-
ment below the line. Each judgment has the form f- 3. A premise of the form Uf- 3i
\;fiE l..n" is an abbreviation for n premises uf- 3 1 ... f- 3/'. Instead, "jE l..n" indicates that
6. UNTYPED CALCULI 63
there are n separate rules, one for each jEl..n. Abbreviations used within a rule are
sometimes given next to the rule name.
A derivation is a tree of judgments, with leaves at the top and root at the bottom,
where each judgment is obtained from the ones immediately above it by a rule. Avalid
judgment is one that can be obtained as the root of a derivation tree. When stating a
judgment, we normally mean that it is valid.
Equational theory
(Eq Syrnm) (Eq Trans)
r-bHa r-aHb r-bHC
r-aHb r-aHc
Equality (H), as we have defined it, is the equivalence relation generated by one-
step reduction (-+). Therefore the Church-Rosser theorem implies that if r- b H Cthen
there exists d such that b -- d and C - - d.
weak in the sense that they do not work under binders. In our setting this means that
when given an object [1;=C;(x;)b; ;fl .. n) we defer reducing the body b; until I; is invoked.
The purpose of the reduction system is to reduce every closed expression to a
result. A result is itself an expression; for the pure c;-calculus, we define a result to be a
term of the form [1;=C;(x;)b; ;f1..n). For example, both [ll=C;(X)[)) and [i2=C;(y)[il=C;(X)[)).l11
are results. (If we had constants such as natural numbers we would naturally include
them among the results.)
Our weak reduction relation is denoted --. We write f- a -- v to mean that a
reduces to a result v, or that v is the result of a. This relation is axiomatized with three
rules:
Operational semantics
(Red Object) (where v == [1;=C;(x;)b; i.1.. n n
f-v--v
(Red Update)
f- a -- [1;=C;(x;)b; ;f!..n) jE1..n
According to the first rule, results are not reduced further. According to the second
rule, in order to evaluate an expression a.lj we should first calculate the result of a,
check that it is in the form [1;=C;(x;)b;{x;} ;f1..n) with jE1..n, and then evaluate bjK[l;=C;(x;)b;
;f!..nn. According to the third rule, in order to evaluate an expression a.lj ~ C;(x)b we
should first calculate the result of a, check that it is in the form [i;=C;(x;)b; ;fl .. n) with
jE1..n, and return [ij=C;(x)b, 1;=C;(x;)b; ;f(1 ..n HiI). Note that we do not compute inside b or
inside the b;.
We observe that the reduction system is deterministic: if f- a -- v and f- a -- v', then
v == v'. The next proposition says that -- is sound with respect to---.
6.2.5 An Interpreter
The rules for v+ immediately suggest an algorithm for reduction, which constitutes an
interpreter for ~-terms . The algorithm takes a closed term and, if it converges, produces
a result or the token wrong, which represents a computation error. We write Outcome(c)
for the outcome of running the algorithm on input c, assuming the algorithm termi-
nates. The algorithm can be defined recursively as follows:
Outcome([li=~(xi)bi iE1..nJ) ~
[li=~(xi)bi iE1..n]
Outcome(a.lj) ~
let 0 = Outcome(a)
in if 0 is of the form [li=~(xi)bi(xi) iE1..n) with jE l..n
then Outcome(bjio»)
else wrong
Outcome(a.lj ~ ~(x)b) ~
let 0 = Outcome(a)
in if 0 is of the form [li=~(xi)bi iE1..n) with jEt .. n
then [lj=~(x)b, li=~(xi)bi iE(1..n)-UI]
else wrong
Clearly, I- c v+ v if and only if Outcome(c) == v and v is not wrong.
«x» ~ x
«b(a)>> ~ «b».«a» where p.q ~ (p.arg:=q) .val
«A(x)b{x}» ~
[arg=~(x)x .arg,
val=~(x)«b{x}»«n--x . arg»)
The initial value of the field arg is unimportant. Here, as in later examples, we use non-
terminating methods to fill in a component (arg) that is conceptually uninitialized;
other fillers could be used in an untyped calculus, but this one applies to typed calculi
as well. Note that the translation maps nested A's to nested r;s: although every method
has a single self parameter, we can emulate functions with multiple parameters.
Renaming (a-conversion) of A-bound variables is valid under the translation (up
to a-conversion) because of the renaming properties of c;-binders. We can verify that~
conversion (that is, (A(x)b{x})(a) = b«aJ) is valid under the translation as well:
let 0 == [arg=«a», val=c;(x)«b{x}»«x~x.argJJ
«(A(x)b{x})(a)>>
- ([arg=c;(x)x.arg, val=c;(x)«b{x}»«x~x.arg»J .arg:=«a»).val
o.val
(<<b{x}»Hx~x.argJ)(x~o»
«b{x}»Hx~o.arg»
«b{x) »Hx~«a»J
«bHa»»
However, TJ-conversion is not valid under the translation, basically because not every
object is the translation of a A-term. For example, the A-terms x and A(y)x(y) are TJ-con-
vertible, but their translations are quite different:
«x» == x
«A(y)x(y)>>
== [arg=c;(y)y.arg, val=c;(y)«x(ymy~y.arg»J
== [arg=c;(y)y.arg, val=c;(y)(x.arg:=y.arg).valj
The properties of the translation have been studied further by Gordon and Rees [65).
We adapt this idea to account for call-by-keyword. We write A(xi=ci iE1..n)b for a
function with multiple parameters Xi with defaults Ci. The applicationj{Yi=ai iE 1..m) can
provide fewer parameters than f expects, and in any order. The association of the pro-
vided actuals to the formals is made by the names Xi, with the defaults being used for
the missing actuals. The interpretation of A-terms with call-by-keyword and default
parameters is as follows.
Translation of call-by-keyword
«A(Xi=CiiE1..n)b{xiiE1..nl» ~ Xi * val, zaV(b)
(Xi=«Ci» iE1..n, val=<;(z)«b{xi i.1..nl»ixi~z,xi iEJ..nH
«b(Yi=ai iEJ..m)>> ~ («b»'Yl:=<<al» ....Ym:=«am»).val Yi * val
6.4 Fixpoints
As a consequence of the translation of A-terms into pure objects, we obtain object-ori-
ented versions of all the encodings that are possible within the A-calculus. None of
these encodings seem, however, particularly inspiring; more direct object-oriented
encodings can usually be found . Such is the case, for example, for fixpoint operators.
The following encoding is much simpler than others that can be obtained by translation
of A-terms:
fix ~
[arg=<;(x)x .arg,
val=<;(x)( (x .arg ).arg:=x. val).val)
We can verify the fixpoint property as follows, recalling that p.q =(p.arg:=q).val is the
encoding of function application:
fix! ~ fix .arg:=f
(arg=f, val=<;(x )«x .arg ).arg:=x. val). val)
fix·f
- fix/,val
« fix/'arg ).arg:=fix/'val).val
(farg:=fix.J).val
- f·(fix.J)
In particular, if we add a constant fix to the A-calculus, and set «fix» ~ fix, then «fix(J)>>
= «j{fix(J»».
As a curiosity, observe the resemblance offix to the translation of A(x)x(x):
«A(x )x(x)>> =
[arg=<;(x)x .arg,
val=<;(x)«x.arg).arg:=x.arg).vall
6. UNTYPED CALCULI 69
We can also provide a translation for recursive terms, ~(x)b, that is more compact
than their natural definition as «fix(A(X)b)>>. We set:
«~(x)b{xl» ~
[ree=c;(x)«b{xl»lxf-x.reeHl·ree
and obtain the unfolding property ~(x)b{xl = bMx)b{xll:
Mx)b{x}»
- [ree=c;(x)«b{xl»(xf-x.reeJ).ree
«b{xl»&xf-x.ree}Kxf-[ree=c;(x)«b{xl»fxf-x.reeHJ&
- «b{xl»Hxf-[ree=c;(x)«b{xl»lxf-x.reeHJ.recj
- «b(xl»fxf-«~(x)b{xl»J
- «bMx)b{xl»»
The recursion operators described use only object primitives. With A-abstraction
and application, we can write the fixpoint operator of Fisher, Honsell, and Mitchell [61]:
fix' ~ A(J)[ree=c;(sy(s.ree)l.ree
whose translation is similar, but not identical, to our fix:
«fix'» = [arg=c;(x)x .arg, val=c;(x)[ ree=c;(s)( (x .arg).arg:=s.ree).vall·reeI
6.S Examples
In later chapters we confront the problem of finding useful type systems for the
untyped co-calculus. We now examine some examples that can be easily written in the
untyped calculus, but pose interesting typing difficulties. Our examples involve updat-
ing through self, and some are based on updating proper methods. The typing require-
ments range from very basic ones, such as typing geometric points, to very sophisti-
cated ones, such as typing an object-oriented version of the numerals inspired by Scott
numerals [124]. For the impatient, typed versions of these examples can be found in Sec-
tions 9.4, 15.2, and 16.5, and imperative variants in Sections 10.4 and 17.4.
In these examples we use boolean, integer, and real constants; one of the examples
illustrates how to embed basic data types within our calculus. The notation of the
untyped A-calculus is also used freely, since it can be encoded.
origin2 ~
[x= 0, y =0,
mv_x = ~(s) A(dx) s.x := s.x+dx,
mv...,y = ~(s) A(dy) s.y:= s.y+dy]
For example, we can define unit2 ~ origin2.mv_x(1).mv...,y(1), and then we can compute
unit2'x = 1.
Intuitively, all the operations possible on origin I are also possible on origin2' Hence
we would like to obtain a type system where a movable two-dimensional point, such
as origin-u can be accepted in any context expecting a movable one-dimensional point,
such as originl'
Later, possibly after modifying the additional attributes, we can extract the object that
was most recently backed up. The retrieve method returns the self that was current
when backup was last invoked, as desired:
o'.retrieve = 0
We can cascade invocations of the retrieve method to recover older and older backups,
eventually converging to the initial object.
We need to define only the numeral zero, because its suee method will generate all
the other numerals. Obviously, the numeral zero should answer true to the iszero ques-
tion, and zero to the pred question.
The suee method is not so easy to manufacture. When suee is invoked on zero, it
should modify zero so that it becomes one. That is, suee should modify self so that it
answers false to the iszero question, and zero to the pred question. Moreover, suee should
be such that when invoked again on numeral one, it produces an appropriate numeral
two, and so on. Hence, for any numeral, suee should update iszero to answer false and
should update pred to return the self that is current when suee is invoked.
zero ~
[iszero = true,
pred = c;(x) x
suee = C;(x) (x.iszero := false).pred := x]
Here the body of suee consists of two cascaded updates to self. We can verify, with a
few tests, that the operational semantics of natural numbers is well represented.
Instead of defining numbers with iszero, pred, and suee, we can define numbers
with only two methods: suee and case. This gives a shorter, although more opaque,
encoding of the natural numbers that does not depend on booleans:
zero ~
[case = Ic(Z) Ic(S) z,
suee = c;(x) x.ease := Ic(Z) Ic(S) sex)]
The case method is in fact a field containing a function of two arguments. For a numeral
n, the first argument is returned if n is zero, otherwise the second argument is applied
to the predecessor of n; that is, n.ease(a)(J) equals a if n is zero, and equals fix) if n is non-
zero and x is its predecessor. The predecessor of n is obtained by the now familiar tech-
nique of capturing a previous self. We can compute:
one ~ zero.suee
= [case = Ic(Z) Ic(S) S(zeTO), suee = (unchanged)]
two ~ one.suee
= [case = Ic(Z) Ic(S) sCone), suee = (unchanged)]
Moreover, we can recover the iszero and pred methods as functions:
iszero ~ Ic(n) n.case(true)("-(p) false)
pred ~ "-(n) n.ease(zero)(A(p) p)
6.5.4 A Calculator
Our next example is that of a calculator object. We exploit the ability to update methods
in order to record the pending arithmetic operation. When an operation add or sub is
entered, the equals method is updated with code for addition or subtraction; this is an
72 IJ ART I. UNTYPED AND FIRS[-ORDER CALCUU
example of mode-switching (see Section 4.7). Two components (arg, acc) are needed for
the internal operation of the calculator, and the other four (enter, add, sub, equals) pro-
vide the user interface. A reset operation could be added to clear the state and restore
the initial equals method.
calculator !
[arg= 0.0,
acc = 0.0,
enter = ~(s) A(n) s.arg := n,
add = ~(s) (s.acc := s.equals).equals *' ~(s') s'.acc+s'.arg,
sub = ~(s) (s.acc:= s.equals).equals *' ~(s') s' .acc-s'.arg,
equals = ~(s) s.argJ
This definition is slightly subtle; it is meant to provide the following calculator-
style behavior:
calculator.enter(5.0).equals = 5.0
calculator.enter(5.0).sub.enter(3.5).equals 1.5
calculator.enter(5.0).add.add.equals = 15.0
method up to date. Every time the set method is invoked, it updates restore to <;(z) z.con-
tents := m, where m is the contents of the cell before the invocation.
myOtherReCell £
[contents = 0,
get = <;(s) s.contents,
set = <;(s) A(n) (s.restore ~ <;(z) z.contents := s.contents).contents := n,
restore = <;(s) s.contents := 0)
In this example current self (s) and future self (z) are statically nested and used in
the same context. Because of this, it is critical that self be a named parameter. Providing
a keyword self, as done in many object-oriented languages, would not be sufficient.
Inheritance for traits works similarly; we simply need not deal with new.
A subclass may override pre-methods instead of inheriting them . For example, c"
below is a subclass of c defined by reusing the first n-l pre-methods of c, and overrid-
ing the last one. An overriding pre-method for In may still refer to the original pre-
method from c as c.l n, or even to some other pre-method of the superclass as c.lp, for
pE l .. n. We can thus model the behavior of super, described in Section 2.3.
6. UNTYPED CALCULI 75
eN £
[new=<;(z)[li=<;(s)z.li(S) it!.n],
Ij =c.1jit1..n-1,
In=A(s) . .. c.ln(s) ... c.lp(s) ... ]
Multiple inheritance is obtained by reusing pre-methods from multiple traits or
classes.
6.6.3 An Example
As an illustration of the techniques introduced in this section, we revisit the example
of storage cells.
In the pseudo-code of the Review, we wrote the class of cells as follows:
class cell is
var contents: Integer = 0;
method getO: Integer is return self.contents end;
method set(n: Integer) is self.contents := n end;
end;
In the untyped <;-calculus we can reformulate it as:
class Cell !,
[new =
<;(z)[contents = <;(s) z.contents(s), get = <;(s) z.get(s), set = <;(s) z.set(s)],
contents = A(s) 0,
get = A(s) s.contents,
set = A(s) A(n) s.contents := n]
This definition resembles the informal one. The main differences are that we have
ignored the type information, that we work in a functional calculus, and that we have
added the method new.
Similarly, we can consider the subclass reCell:
subclass reCell of cell is
var backup: Integer = 0;
override set(n : Integer) is
self.backup := self.contents;
super.set(n);
end;
method restoreO is self. contents := self. backup end;
end;
We can reformulate it as:
76 PART I. UNTYPED AND FIRSI' -ORDER CALCULI
classReCell ~
[new =
C;(z)[contents = C;(s) z.contents(s), get = C;(s) z.get(s), set = C;(s) z.set(s),
backup = C;(s) z.backup(s), restore = C;(s) z.restore(s)],
contents = classCell.contents,
get = classCell.get,
set = A(S) A(n) classCell.set(s.backup := s.contents)(n),
backup = A(S) 0,
restore = A(S) s.contents := s.backup]
The attributes contents and set are explicitly inherited from classCell. The attribute set is
overridden; the invocation of classCell.set in the new code corresponds to the use of
super in the pseudo-notation. The attributes backup and restore are added.
The simple equalities shown, based on the standard ~-reduction of A-terms, reveal
that the self-application semantics matches the primitive semantics. Hence untyped
objects can be faithfully interpreted using A-abstraction, application, and record con-
structions. Records themselves can be seen as functions over labels and thus reduced
6. UNTYPED CALCULI 77
to pure A-terms. If we are concerned only with untyped object calculi, little else needs
to be said.
Unfortunately, the match between primitive and self-application semantics does
not extend directly to typed calculi. By interpreting c;-binders directly as A-binders, the
self-application semantics causes the type of each method of an object to have the form
A~Bi, where A is the type of the object (the self parameter), and Bi is the result type of
the method. In the terminology of Section 2.6, the type A occurs in contravariant posi-
tion in A~Bi; because of contravariance, natural subtype relations between object types
fail. We return to this point in Chapter 18, after developing the necessary formal back-
ground.
Recursive-record semantics
(Ii distinct)
£ Jl(x)(li=b;{xi iE1..n) (li=b;{oH iE1..n)
£ o.lj bjM (jEl .. n)
So far, the semantics of method invocation matches the primitive semantics. More-
over, the expected subtype relations are validated, because the troublesome contravar-
iant parameters are "hidden". If we require only an object calculus without update,
then the recursive-record semantics will do. Unfortunately, neither field update nor
method update work as expected in this semantics; the most plausible definition
appears to be the following:
Recursive-record semantics (update)
o.lj~C;(y)b{y} £ Jl(x)o.lj:=(A(y)b{y})(x) (jEl .. n)
(lj=b«o.lj~C;(y)b{ym, li=b;Ko& ie(l..nHjl)
~ Jl(x)(lj=bH4l;=b;Kx& ie(Ln}-lil)
The result of updating a method is no longer in the form of an object: record update
fails to update "inside the 11" so that all the other methods can become aware of the
update. We have, for example:
o £ [11=C;(x)3,12=C;(x)x.lJl
p £ O.11~C;(x)5
78 PART I. UNTYPED AND FIRST-ORDER CALCULI
We now begin to investigate the type theory of the ~-calculus. We start with a simple
first-order type system with object types. We prove two simple properties of this sys-
tem: a unique-types theorem and a subject reduction theorem. We also define an equa-
tional theory, and extend the encodings of functions and fixpoints to typed encodings.
Judg (rule)
for a derivation tree having the derivation subtrees T 1, ..• , Tn for n 2: 0, and having a
conclusion judgment Judg obtained by (rule) from the conclusions of T 1, • • • , Tn. To make
the presentation more compact, we also use the following notation for derivation sub-
trees:
Judg reason
to indicate that the derivation tree leading to the conclusion Judg is omitted. The reason
cited for the derivation may be "by (rule)" indicating the last rule leading to Judg, or a
comment like "routine" or "by previous derivation" . A derivation tree could have the
rr
shape:
(rulel)
(ludg,
Judg2 (rule2)
Judg3 (rule3)
(Judg 4 reason
Judgs (rules)
Judg6 (rule6)
where Judg6 is obtained by (rule6) from Judg3 and Judgs, and where Judg 1 is obtained by
(rulel) with no premises.
A valid judgment is one that can be obtained as the root of a derivation tree in a
given formal system. When stating a judgment, we normally mean that it is valid.
Informally, we may write 3 for E f- 3, when the environment E is clear from context or
unimportant.
Two kinds of judgments are used in the fragment ~Ob: a type judgment E I- B, stat-
ing that B is a well-formed type in the environment E, and a value typing judgment E
I- b : B, stating that b has type Bin E. The form of our judgments is chosen in anticipation
of future uses of our fragments . For example, at the moment the well-formedness of a
type B is independent of any environment E, but this will not always be true; so we pre-
fer to write E I- B rather than to omit E.
The first rule, (Type Object) states that the object type (li:Bi ;€J..n] is well-formed in
the environment E, provided that each Bi is well-formed in E. We always assume, when
writing (1;:Bi i€J..n), that the labels Ii are distinct. We identify object types (li:B; i€J..n] up to
reordering of their components li:B;.
According to the rule (Val Object), an object of type (1;:Bi i€l..n] can be formed from
a collection of n methods whose self parameters have type [li:Bi i€l..n] and whose bodies
have types B1, • .. , Bn . Note the circularity introduced by the self parameter: in order to
construct a value of type (li:Bi i€J..n) we assume the existence of values of type [1;:Bi i€l..n] .
The rule (Val Select) describes how to give a type to a method invocation. When a
method Ii of an object of type [li:Bi i€ l..n] is invoked, it produces a result of type Bi.
Finally, the rule (Val Update) deals with method update. Method update preserves
the type of the object that is updated. The type of the object cannot be allowed to
change, because other methods assume it. The method used for the update is checked
just as in the rule (Val Object).
82 PART I. UNTYPED AND FIRST-ORDER CALCULI
Notation
• [ ... , l=b, .. .1stands for [... , 1=~(y:A)b, ... 1, for an appropriate A and a y;'FV(b).
• [ .. . ,l,m:B, ... 1stands for [. . . ,I:B, m:B, ... 1, in examples.
• a.l:=b stands for a.l~~(y:A)b, for an appropriate A and some y;'FV(b).
• We identify ~(x:A)b with ~(y:A)(b«Xf-Y»), for any y;'FV(b).
• We identify any two objects or object types that differ only in the order of their
components.
The definitions of free variables and substitution are similar to the ones in Section
6.2.1. From now on we often omit these routine definitions.
The first rule, (Env ¢), states that the empty environment is a well-formed environ-
ment. In all our type systems, this is the only rule with no assumptions. Therefore every
derivation tree must use (Env ¢) at its leaves.
The rule (Env x) describes how to add an assumption x:A to an environment: we
must check that the variable x is fresh and that A is a well-formed type.
The rule (Val x) is used to extract an assumption from an environment. The nota-
tion E', x:A, E" means that x:A occurs somewhere in the environment (and we know
that there is a unique occurrence of x, by the assumptions of (Env x)). The conclusion
is that x has type A.
The fragment dK introduces a ground type K.
(Type Const)
EI-o
EI-K
7. FIRST -ORDER CALCULI 83
It may seem puzzling that no elements are given for the ground type K. The stan-
dard use for K is as a starting point for building function types, such as a type of Church
numerals (K~K)~(K~K) [24). A ground type is not strictly necessary as a starting
point for building object types, because the type [] is available; however, we include K
in some of our object calculi in order to simplify comparisons with A-calculi. In exam-
ples, we informally use other specific ground types, such as Bool, Nat, Int, and Real,
with associated constants and operations. These could be added to our calculi with
standard fragments, or through encodings.
The fragment 11.... describes functions and function types.
By the rule (Type Arrow), a well-formed function type A~B can be obtained from
any two well-formed types A and B. The rule (Val Fun) describes how to prove that a
function A(x:A)b, with parameter x of declared type A and body b, has type A~B; it is
sufficient to check that the body b has type B under the assumption that x has type A.
The rule (Val Appl) states that a term b of function type A~B can be applied to an argu-
ment a of type A; the result has type B.
Collecting the fragments given above, we now define three typed calculi:
ObI ~ 11K V I1x V 110b the first-order typed ~-calculus
FI ~ 11K V I1x v 11.... the first-order typed A-calculus
FOb} ~ 11K V I1x v 11.... V 110b the first-order typed A.~-calculus
We summarize the definition of FOb}, the largest of these calculi, by giving its syntax.
Syntax of the FOb} calculus
A,B ::= types
K ground type
[li:Bi iE1..n] object type (Ii distinct)
A~B function type
a,b ::= terms
X variable
[li=~(xi:Ai)bi iEl..n] object (Ii distinct)
a.l method invocation
a.l;,~(x:A)b method update
A(x:A)b function
b(a) application
84 PART I. UNTYPED AND FIRST -ORDER CALCULI
Unless noted otherwise, each of our calculi comes with an equational theory made
up from equational fragments. For simplicity, when assembling a calculus we list only
its typing fragments; the equational theory is uniquely determined, at least in this
book. The equational theories for ObI, F}, and FOb} are defined later in this chapter. In
general, the equational fragments of each theory guarantee at least that equality is a
congruence. Moreover, we adopt the convention that a calculus that includes a typing
fragment lls also includes the corresponding equational fragment tb s, if it exists.
7.4 Examples
The first-order calculi introduced in the previous section are too weak for most inter-
esting examples. Here we give two simple typed examples as a drill in applying the
typing rules for objects.
In these examples, and in the rest of the book, we often use the following conven-
tion for typed definitions.
Notation
• x: A ~ a stands for x ~ a and E I- a : A
where E is determined from the preceding context.
7.4.2 Booleans
Booleans and conditionals can be encoded with objects. In ObI, we do not have a single
type of boo leans; instead, we have a type BoolA for every type A. We define:
BoolA ~ [if A, then:A, else:AJ
7. FIRSf -ORDER CALCULI 85
The terms of type BoolA can be used in conditional expressions whose result type
is A. The booleans trueA and falseA are objects with three methods: if, then, and else. By
indirection through self, trueA.if returns trueA.then, and falseA.if returns falseA.else. More
precisely, we have:
trueA : BoolA g,
[if = r;(x:Bool A) x.then, then = r;(x:Bool A) x t.hen, else = r;(x:Bool A) x e. lse]
falseA : BoolA g,
(if = r;(x:Bool A) x e. lse, then = r;(x:BooIA) x t.hen, else = r;(x:Bool A) x e. lse]
This typing is derivable for any given type A. For c and d of type A, we define:
ifA b then c else d : A ~
«b. then ~ r;(x:BoolA)c)·else ~ r;(x:BooI A) d).if xttFV(c)uFV(d)
This conditional construct updates the attributes then and else of the boolean b, and
invokes the if method. These definitions give the expected behavior of booleans:
ifA trueA then c else d c
ifAfalseA then c else d = d
However, the uniqueness property fails if we omit type annotations for object construc-
tion, with the rule:
86 PART 1. UNTYPED AND FIRST-ORDER CALCULI
For example, in the modified system, [I=~(x)x.l] has type [I:A] for any A. Still, the con-
vention of omitting ~-binders entirely for methods that do not depend on self is innoc-
uous. For example, [1=3] has unique type [1:Int] if Int is the type of 3.
(Red Update)
I- a -- [li=~(Xi:Ai)bi iEt .. n] jEl..n
Proof
The proof is by induction on the derivation of I- c v-+ v. There is a case for each of
the rules of the operational semantics:
Case (Red Object)
This case is trivial, since c = v.
Case (Red Select)
n
Suppose I- a.lj v-+ v because I- a v-+ [h=C;(xj:Aj)bj{xj) jE1..nj and I- bjWj=C;(xj:Aj)bj{xj) jE1.. n
v-+ v. Assume that fill- a.l j : C. The last step in the derivation of this judgment must
be an application of (Val Select). The premise of (Val Select) must be fill- a : A for
some A of the form [Ire, ... j. By induction hypothesis, we have fill- [lj=C;(xj:Aj)bi/xj)
jEt .. nj : A. The last step in the derivation of this judgment must be an application of
(Val Object). This implies that all Aj equal A and one of the premises of (Val Object)
must be fIl, xj:A I- bj : C. By Lemma 7.5-2, it follows that fill- bjWj=C;(xj:A)bi/xj} jE1.. n n:
C. By induction hypothesis, we obtain fill- v : C.
Case (Red Update)
Suppose I- a.lj ~ C;(x:A)b v-+ [lj=C;(x:Aj)b, Ij=C;(xj:Aj)bj jE(1 ..nHj'j because I- a v-+
[lj=C;(xj:Aj)bj jEl..nj. Assume that fill- a.lj ~ C;(x:A)b : C. The last step in the derivation
of this judgment must be an application of (Val Update). Then C equals A and the
premises of (Val Update) must be fill- a: A and fIl, x:A I- b : B, with A of the form [lj:B,
... j. By induction hypothesis, we have fill- [lj=C;(xj:Aj)bj jEl..nj : A. The last step in the
derivation of this judgment must be an application of (Val Object). This implies
that A must have the form [IrB, Ij:Bj jE(l..n H ;'j. Moreover all Ai equal A, and the pre-
mises of (Val Object) must be fIl, xj:A I- bj:Bj for iEl..n. Therefore by an application
of (Val Object) we obtain fIl I- [1;=C;(x:A)b, Ij=C;(xj:A)bj je(l..n)-U'j : A, that is, fIl I-
[lj=C;(x:A)b, h=C;(xj:A)bj jE(1..n)-U'j : C.
o
Therefore if a term has a type, and the term reduces to a result, then the result has
that type too. This statement is vacuous if the term does not reduce to a result. This can
happen either because reduction diverges (the rules are applicable ad infinitum), or
because it gets stuck (no rule is applicable at a certain stage). We would like to prove
that the latter is in fact not possible. In other words, we would like a stronger result: if
the reduction does not diverge, then it produces a result of the correct type without get-
ting stuck. The absence of stuck states is the essence of what is often called type sound-
ness: well-typed programs that do not diverge produce a result of the expected type.
In the rest of this section we show that reductions never get stuck by considering
an algorithm for reduction; in the algorithm, the result wrong represents stuck states.
The algorithm is that of Section 6.2.5, extended to deal with type annotations:
Outcome([lj=c;(xj:Aj)bj jEl..nj) ~
[lj=C;(xj:Aj)bj jE1..nj
88 PART I. UNTYPED AND FIRST-ORDER CALCULI
Outcome(a'!j) ~
let 0 = Outcome(a)
in if 0 is of the form [1;=~(x;:A;)b;{x;} ;EI.."] with jE 1..n
then Outcome(bjM)
else wrong
Outcome(a.lj::= <;(x:A)b) ~
let 0 = Outcome(a)
in if 0 is of the form [/;=<;(x;:A;)b; ;.1.."] with jE 1..n
then [lj=<;(x:Aj)b, 1;=<;(x;:A;)b; ;.(I..")-(il]
else wrong
If Outcome(a) is defined, then it is either wrong or a result. We obtain:
Theorem 7.5-4 (Obi reductions cannot go wrong)
If f/l f- c : C and Outcome(c) is defined, then f/l f- Outcome(c) : C, hence Outcome(c) l'
wrong.
Proof
The proof is by induction on the execution of Outcome(c), and is very similar to the
proof of Theorem 7.5-3. Since the proof is by induction on the execution of an algo-
rithm written in an informal notation, the proof itself is somewhat informal.
Case c == [li=~(Xi:Ai)bi i£1 ..n]
This case is trivial, since c = Outcome(c).
Case c == a.lj
Since Outcome(c) is defined, so is Outcome(a). Let 0 = Outcome(a). By hypothesis f/l f-
a'!j : C. Then f/l f- a : A for some A of the form [/j:c, ... ]. By induction hypothesis, f/l f-
0: A . Therefore 0 has the form [/;=<;(x;:A ;)b;{x;} ;.I..n] with jE Ln. This implies that Aj
equals A and that f/l, xj:A f- bj : C. By Lemma 7.5-2, it follows that f/l f- bjloi : C. Since
Outcome(c) is defined, so is Outcome(bjloi). By induction hypothesis, f/l f- Out-
come(bjloi) : C, that is, f/l f- Outcome(c) : C.
Case c == a.lj ::= ~(x:A)b
Theorem 7.5-3 can be derived from Theorem 7.5-4 using the fact that if f- c vo+ v then
v : Outcome(c). On the other hand, as the proof of Theorem 7.5-4 illustrates, it is gener-
ally routine but cumbersome to adapt a subject reduction result in order to prove the
absence of stuck states. Therefore in the sequel we prove subject reduction properties
but not the absence of stuck states.
11=
(EqSymm) (Eq Trans)
Ef-aHb : A Ef-aHb : A Ef-bHc:A
Ef-bHa : A Ef-aHc:A
(Eqx)
E', x :A, En f- 0
E', x:A, En f- X H X : A
The first three rules for objects are congruence rules, establishing that two expres-
sions are equal when all their corresponding subexpressions are equal; the next two
rules are evaluation rules for selection and update, corresponding to the operational
semantics of the untyped calculus of Section 6.2.4.
I1=Ob
(Eq Select)
Ef-aHa ' :[Ii: Bi iel..nl jEl..n
E f- a.lj H a'.lj : Bj
For functions, we have the standard theory for the first-order A-calculus:
(EqFun) (EqAppl)
E, x:A f- b H b' : B E f- b H b' : A-+B E f- a H a' : A
E f- A(x:A)b H A(x:A)b' : A-+B E f- b(a) H b'(a') : B
The fragments ~= and ~=x are used in the equational theories of all our calculi. In
addition, ~=Ob is used for Ob}, ~=~ is used for FJ, and both are used for FOb}.
b : A g, [x=l,!=<;(s:A)1)
c :A g, [x=l,f=<;(s:A)s.x)
We might think that band c are equal at type A because j1S I- b.x H c.x : Nat and j1S I-
bf H cf: Nat. But to prove their equality, the (Eq Object) rule requires showing j1S, s:A
I- 1 H s.x : Nat. This cannot be obtained because we have no assumptions about the
value of self, in particular that s.x is currently 1. In fact, self may change and invalidate
this assumption about its value. For example, it is possible to distinguish b from c after
updating them both with equal values:
b' : A g, b.x:=2
c' : A g, c.x:=2
Now we have j1S I- b' f HI: Nat and j1S I- c'f H 2 :Nat, so asserting j1S I- b He: A would
lead to a contradiction.
This example illustrates a fundamental difference between the equational theories
of object calculi and record calculi, as well as a fundamental difficulty in reasoning
about objects. Still, we would like to say more about object equivalence than d=Ob
allows. For example, we may wish to determine that band c are interchangeable in con-
texts that read or modify only their x components; that is, in contexts where band care
considered as having type [x:Natl . This equation involves an implicit or explicit
assumption that a longer object belongs to a shorter type. We examine this idea in
Chapter 8.
«XA»p ~ p(X)
«bA ..... B(aA)>>p ~
(«b»p.arg~~(x:<<A ~ B» )«a»p).val
«A.(x:A)bB»p £
[arg=~(x:«A~B» )x.arg,
val=~(x:«A~B»)«b»pln_x.Qrgl]
Both «fiXA»p and the ~-bound variables of this term have type:
«(A-?A)~A» = [arg:[arg:«A», val:«A»J, val:«A»]
Similarly, a typed version ll(x:A)b of the recursive terms Il(x)b of Section 6.4 can be
expressed in ObI:
«1l(x:A)b»p ~
[rec=~(x:[rec:«A» ])«b»p!n--x.recJl.rec
After these general preliminaries, we write the subtyping rules for function and
object types:
(Sub Arrow)
E I- A' <: A E I- B <: B'
E I- A~B <: A'~B'
Let us write A{.} for an incomplete type where zero or more subexpressions are
missing, and represented as holes (.), such as «.~Top)~.). Then AtBa is the type
obtained from A{.} by filling all the holes with B. We say that A{., is covariant if B <: B'
implies AlB} <: AlB'} for all B, B'. We say thatA{.} is contravariant if B <: B' implies AlB'}
<: AiB} for all B, B'. Otherwise, we say that A{.} is invariant.
The subtyping rule (Sub Arrow) for function types is the standard one. According
to this rule, a function type is contravariant in its domain and covariant in its
codomain. More precisely, .~B is contravariant for any type B, and A~. is covariant
for any type A (and .~. is invariant). As a consequence, A~B <: A'~B if A' <: A, and
A~B <: A~B' if B <: B'.
The subtyping rule for object types (Sub Object) allows a longer object type [li:Bi
i'J..n+m] to be a subtype of a shorter object type [/i:B; ieJ..n]. In general an object type has
multiple incomparable supertypes, for example [11:B I , 12 :B2 ] is a subtype of both [1 1:B1]
and [12:B2] .
Object types are invariant in their component types: the subtyping [li:Bi ieJ..n+m] <:
[/i:Bi' iEt ..n] requires Bi == B/ for all iEl..n. That is, object types are neither covariant nor
contravariant; in particular, [/:A~.] is not covariant and [1:.~B] is not contravariant.
The necessity of this invariance condition is explained in Section 8.6.
We define the calculi:
The syntax of the new calculi with subtyping differs from the old calculi only by the
addition of the type Top.
The translation of FI into ObI of Section 7.7 does not extend to a corresponding
translation of FI<: into Obl<~ because «A-)B» = [arg:«A», val:«B»J is invariant in «A» and
«B». Hence Ob}<: is essentially a restricted version of FOb l <: with invariant function
types. We recover the covariant / contravariant subtyping properties of function types
in Section 13.2 by an encoding based on bounded universal and existential types, and
in Section 8.7 by a simpler encoding based on variance annotations.
8.2 Examples
As an exercise in the use of subtyping, we treat a variant of the cell example of Section
6.5.5. (We cannot treat the cell example itself because we do not yet have recursive
types.)
A RomCell is a storage cell that can only be read. A Prom Cell can be written once,
and then becomes a RomCeli. For the purpose of information hiding, neither RomCell
nor Prom Cell exposes the contents field. A third type, PrivateCell, is used for operating
on that field.
RomCell ~ [get:NatJ
Prom Cell ~ [get:Nat, set:Nat-)RomCellj
PrivateCell ~ [contents :Nat, get:Nat, set:Nat-)RomCellJ
We obtain the subtypings PrivateCell < : PromCell <: RomCeli. Thus a PrivateCell is a
PromCell and a RomCell simply by subsumption.
We define a cell of type PrivateCell, and immediately subsume it into PromCell:
myCell : PromCell ~
[contents = 0,
get = c;(s:PrivateCell) s.contents,
set = c;(s :PrivateCell) A(n:Nat) s.contents := nJ
The subtyping PrivateCell <: RomCell is needed for typing the body of the set method,
because the body of set has type PrivateCell whereas the expected return type is Rom-
Cell . We can now write, for example, myCell.set(3).get.
that it holds for FOb1<:. The minimum-types property is potentially useful for develop-
ing typechecking algorithms, since it guarantees the existence of a canonical type for
each typable term.
In order to prove the minimum-types property for Ob 1<" we consider a system
MinOb1<: obtained from Ob 1<: by removing (Val Subsumption), and by modifying the
(Val Object) and (Val Update) rules as follows:
Modified rules for MinOb 1<:
(Val Min Object) (where A'" [/i:Bi i<I.."])
E, Xi:A I- bi : Bi' S!S I- Bi' <: Bi 'ViEl..n
E I- [li=~(xi:A)bi ie1..nj : A
Proof
Assume E I- a : A. By Proposition 8.3-3, E I- a : B is derivable in MinOb)<: for some
B such that E I- B <: A. By Proposition 8.3-1, E I- a : B is also derivable in Ob)<:. By
Proposition 8.3-3, if E I- a: A ', then E I- a: B' is also derivable in MinOb)<: for some
B' such that E I- B' <: N . By Proposition 8.3-2, B == B', so E I- B <: A'.
o
Just as lack of annotations for ~-binders destroys the unique-types property for
Ob], it destroys the minimum-types property for Ob)<:. For example, let:
A == [/:[])
A' == [/:A]
a == [l=<;(x)[l=<;(x)[lll
then:
Jljl-a:A and Jljl-a:A '
but A and A' have no common subtype. This example also shows that minimum typing
is lost for objects with fields (where the <;-binders are omitted entirely) because a can be
written as [l=[l=[]]] with our conventions.
The term a.l:=[] typechecks using JIj I- a: A but not using JIj I- a: A'. Naive type infer-
ence algorithms might find the type A' for a, and fail to find any type for a.I:=[]. Thus
the absence of minimum typings poses practical problems for type inference. Palsberg
has described an ingenious algorithm for type inference that surmounts these prob-
lems [98] .
In contrast, in Ob)<: (with annotations), both JIj I- [l=<;(x:A)[l=<;(x:A)[lll : A and JIj I-
[/=<;(x : A')[/=~(x:A)[lll : A' are minimum typings. The former typing can be used to con-
struct a typing for a.l:=[], by deriving JIj I- [/=<;(x : A)[/=<;(x : A)[lll~<;(x :A)[] : A.
The term a./:=[] is also an example of a term typable in Ob)<: but not in Ob).
Still, this does not give us enough power to compare objects of different lengths
(except as members of Top). We remedy this by augmenting the t.=Ob fragment from
Section 7.6.1 with the rule (Eq Sub Object), which allows us to ignore methods that do
not appear in the type of an object. At the same time we generalize (Eval Select) and
(Eval Update) to deal with objects and types of different lengths.
~<:Ob
(Eq Sub Object) (where A;: [/i:Bi iELnl, A ' ;: [/i:Bi iEl..n+m])
E,Xi:Al-bi:Bi ViEl..n E,xj'A'l-b;:B; VjEn+l..n+m
E I- [1,=C;(xi:A)bi iEl..n) H [li=C;(xi:A')bi iEl..n+m) : A
(Eval Select) (where A;: [/i:Bi iE1.. nl. a;: [li=C;(xi:A')bilxil iE1..n+m])
El-a:A jE1..n
E f- a.l; H b;M : B;
(Eval Update) (where A;: [/i:Bi iEl ..n), a;: [li=<;(xi:A')b i iE1..n+m])
El-a:A E,x:Al-b:B; jE1..n
According to (Eq Sub Object) an object can be truncated to its externally visible collec-
tion of methods, but only if those methods do not depend on the hidden ones. (The
truncated object would not work otherwise.) It is hard to obtain more than this, as we
discuss in the next section.
The fragment t.=<: is used in the equational theories of all our calculi with subtyp-
ing. In addition, t.=<:Ob is used for Ob 1<: and FOb1<:.
b:A ~ [x=l,f=~(s:A)l)
c: A ~ [x=l,f=~(s:A)s.x)
Using (Eq Sub Object) it is at least possible to show that band c are equal at type [x:Natj,
by showing ¢ I-- b H [x=l) : [x:Nat) and ¢ I-- C H [x=l) : [x:Nat).
We can ask next whether band c are equal at type [fNat). This seems reasonable
because the only way to distinguish band c is to update their x components, which are
not exposed in [fNat]. However, we cannot apply (Eq Object) and (Eq Sub Object)
because we would need to show ¢, s:[fNat) 1--1 H s.x: Nat.
A stronger rule would be required to show ¢ I- b He: [fNat), but it is not clear
how to write such a rule satisfactorily. Fortunately, equations such as ¢ I-- b He: [fNat]
can be handled by more sophisticated proof techniques based on bisimilarity [65].
The subtyping A' <: A is needed for typing the components Ij of c', which contain inher-
ited pre-methods.
This technique generalizes straightforwardly to multiple inheritance. A class c'
may reuse pre-methods from several other classes, say C1 and C2 of types Class(A 1) and
Class(A2), respectively. These classes C1 and C2 need not even have the same type: if A '
<: A1 and A' <: A:2, then we have that Class(A') may inherit from both Class(A 1) and
C/ass(A2)'
In Section 6.6 we described an interpretation of super; that interpretation is well-
typed according to the class types just described. For example, the overriding pre-
method 11 of the subclass c' may contain an invocation to the original pre-method 11 of
the superclass c:
11 = A(s:A') .. . c.11(s) .. .
The application c.Ms) is our interpretation for superh Since c.11 has type A-7B1, and A'
<: A, the application C.ll(S) is well-typed.
classes, therefore, can be used for inheritance but cannot be instantiated. In the litera-
ture one can also find concrete classes that can be both instantiated and used for inherit-
ance (like our normal classes), and leaf classes that can be instantiated but not used for
inheritance [116). An element of type [new:A] can be seen as a leaf class: it can be instan-
tiated, via new, but cannot be used for inheritance because its pre-methods have been
forgotten.
Generalizing from these considerations, we may associate two separate interfaces
with a class type. The first interface is the collection of methods that are available on
instances. The second interface is the collection of methods that can be inherited in sub-
classes. We generalize our definition of class types to account for these additional
parameters.
For an object type A == [li:Bi iEI] with methods Ii iEI (where I is an index set) we con-
sider a restricted instance interface, determined by a set Ins ~ I, and a restricted subclass
interface, determined by a set Sub ~ I. Alternatively, we may think that each method in
a class definition has two annotations: whether the method can be inherited in sub-
classes, and whether the method can be used on instances. These annotations deter-
mine the sets Ins and Sub.
Therefore if A == [li:Bi iEI] is an object type, we define:
Class(A)lns,Sub £ [new:[li:Bi iElns], li:A~Bi iESub] for Ins,Sub ~ I
Class(A) £ Class(A)l,l
At the term level we define:
class(A, ai iEI)lns £ [new=<;(z:Class(A)lns,I)[li=<;(s:A)z.li(s) iEI], li=ai iEI]
class(A, ai iEI) £ class(A, ai iel)1
where ai : A~Bi iel.
Note that the inclusion Class (A)I,I <: Class(A)lns,Sub does not hold in general, because
of invariance in the type of new. However, for any Ins,Sub s;;; I we do obtain:
c/ass(A, ai iel)lns : Class(A)lns,Sub
because c/ass(A, ai iEI)lns has type Class(A)lns,1 by construction, and because Class(A)lns,1
<: Class(A)lns,Sub. In essence, any class built according to our standard pattern can be
given restricted instance and subclass interfaces.
Particular values of Ins and Sub correspond to common situations. Among others,
the common notions of abstract, concrete, and leaf classes, as well as public, protected,
and private methods [97, 109, 115), can be characterized as follows:
c :Class(A)91,Sub is an abstract class based on A
c :Class(A)lns,91 is a leaf class based on A
c: Class(A)/,l is a concrete class based on A
c: Class(A)pub,Pub has public methods Ii i.Pub
and private methods Ii iE/-Pub
8. SUBTYFING 103
8.5.3 An Example
As an exercise in the use of classes, we continue the cell example of Section 8.2; we illus-
trate the use of abstract classes, leaf classes, and their hybrids.
Corresponding to the type PrivateCell, we have the following class type:
PrivateCellClass £
[new:PrivateCell,
contents:PrivateCell~Nat,
get : PrivateCell~Nat,
set:PrivateCell~Nat~RomCelll
PrivateCellTrait !
[contents:PrivateCell~Nat,
get:PrivateCell~Nat,
set:PrivateCeli ~ Nat ~ RomCe/I]
privateCellClass : PrivateCeliTrait
Also by subsumption, privateCellClass can be seen as a leaf class, that can only be instan-
tiated:
PrivateCeliLeafClass !
[new:PrivateCell]
privateCellClass : PrivateCellLeafClass
privateCellClass.new : PrivateCell
The contents attribute is exposed in any object generated from privateCellClass.
Since a user should interact with a cell only through the methods get and set, it is sen-
sible to hide contents; for this purpose, we define:
RestrictedCellClass !
[new:PromCell,
contents:PrivateCell~Nat,
get : PrivateCell~Nat,
set:PrivateCell~Nat~RomCell]
restrictedCellClass : RestrictedCellClass !
[new =
q,(z:RestrictedCellClass)
[contents = q,(s:PrivateCell) z.contents(s),
get = q,(s:PrivateCell) z.get(s),
set = q,(s:PrivateCell) z.set(s)],
contents = "A.(s:PrivateCell) 0,
get = A.(s:PrivateCell) s.contents,
set = "A.(s:PrivateCell) "A.(n:Nat) s.contents := nJ
In our notation, RestrictedCellClass is Class(PrivateCell)lget,set},lcontents,get,setl' The code for
restrictedCellClass is essentially identical to the code for privateCellClass. The only differ-
ence is in the type annotation for the methods of the class (e.g., for new), which has
changed from PrivateCellClass to RestrictedCellClass. As a result of this change, we have
restrictedCeliClass.new: PromCell; thus contents is not visible in objects generated from
restrictedCeIlClass.
Still, other classes can inherit the (trivial) implementation for contents because
restrictedCeIlClass.contents: PrivateCell~Nat. Applying subsumption once more, con-
tents can be made invisible to other classes; we define:
8. SUBTYPING 105
NoContentsCellCiass g,
[new:PromCell,
get:PrivateCell--7Nat,
set:PrivateCell--7Nat..-?RomCelll
restrictedCellClass : NoContentsCellCiass
In our notation, the type NoContentsCellCiass is Class(PrivateCell){get,setl,{get,setl'
Note that the domain of the pre-methods restrictedCellClass.get and restricted-
CellClass.set remains PrivateCell, which has a contents attribute. A class that inherits one
of these pre-methods would have to provide its own implementation for contents. This
implementation can be different from the original one.
For example, a cell may store a number in compressed form in a field rep. The
attribute contents becomes a method that decompresses the number. We assume two
primitives, compress and uncompress, for compressing and decompressing numbers.
The method set is overridden so as to store inputs in rep. The method get is inherited
from restrictedCellCiass.
CompressedPrivateCell g,
[contents:Nat, rep:CompressedNat, get:Nat, set:Nat--7RomCelll
CompressedPrivateCellClass £
Class( CompressedPrivateCell)
compressedPrivateCellCiass : CompressedPrivateCellClass g,
[new =
<;(z:CompressedPrivateCellCiass)
[contents = <;(s:CompressedPrivateCell) z.contents(s),
rep = <;(s:CompressedPrivateCell) z.rep(s),
get = <;(s:CompressedPrivateCell) z.get(s),
set = <;(s:CompressedPrivateCell) z.set(s) I,
contents = t..(s:CompressedPrivateCell) uncompress(s.rep),
rep = t..(s:CompressedPrivateCell) compress(O),
get = restrictedCellClass.get,
set = t..(s:PrivateCell) t..(n:Nat) s.rep := compress(n)1
Finally, we define a leaf class:
PromCellLeafClass g,
[new:PromCelll
restricted Cell Class : PromCellLeafClass
Given a class of type PromCellLeafClass, we cannot tell whether the class or its instances
have a contents attribute; we can only create an object of type PromCell.
106 PART I. UNTYPED AND FIRST-ORDER CALCULI
8.6.1 Records
To permit a precise comparison between objects and records, we introduce the basic
rules for record types with subtyping.
As in Section 6.7.1 we write (li=ai iEl..n) for the record with labels Ii and fields ai; we
write r.lj for record selection, and r.lj:=b for functional record update. Functional record
update can be defined from the other operations.
A record has type (li:Bi iEl..n) if it has components Ii with types Bi. Unlike our object
types, record tYpes have a covariant sub typing rule.
~Rcd
The basic equational rules for records are cut-down versions of the rules for
objects, for the special case where all attributes are fields .
~=Rcd
(Eq Record)
E I- bi H b;' : Bi \;fiE l..n
8. SUBTYPING 107
(Eq Record Select) (Eval Record Select) {where a'" (li=b i i'I.."»
E I- a H a' : (lj:Bj iE!..n) jE l..n E I- a: (li:Bi iE!..n) jEl..n
E I- a.lj H a' .lj : Bj E I- a .l j H bj: Bj
(Eval Extract) (where A'" [li:Bi iEl..n], a'" [li=~{xi:A ' )bi iEI.." +mJ)
E I-a: A jEl..n
E I- a.lj H A(xrA)bj : A~Bj
These rules amount to interpreting an object type A;;: [li:Bi iE1.."] as a recursively defined
record-of-functions type A;;: (li:A~Bi iE1..").
108 PART I. UNTYPED AND FIRSf-ORDER CALCULI
8.6.3 Elder
Although in general it is unsound to extract a method, it is sound to refer to the previ-
ous value of a method in the course of an update. Just as the new value of a record field
can be defined from its old value, the new code for a method can reuse the updated
code. We write a.lj~(y:Bj)<;(x :A)b for the result of updating the I j method of a. When Ij is
subsequently invoked, x is self and y (which we call elder) is bound to the body of the
old method Ij •
The type and evaluation rules for update with elder are:
Rules for elder
(Val Update with elder) (where A", [li:Bi iEl..n))
E f- a: A E, x:A, y:Bj f- b: Bj jEl..n
E f- a.lj~(y : Bj)c>(x:A)b : A
(Eval Update with elder) (where A'" [li:Bi iE1 .. n), a'" [li=<;(xi:A' )b i iEl..n+m), Xjf.dom(E))
E f- a : A E, x:A, y:Bj f- b(x,y) : Bj jE l..n
E f- a./j~(y:Bj)<;(x:A)b(x,y} H [/j=<;(xj:A')b(xj,bj&, li=<;(x;:A')b; ;E(1..n+m).-{j\) : A
Update with elder could easily be implemented using method extraction. But,
unlike method extraction, update with elder is sound because we never apply the old
method to an arbitrary element of type A. The old method's self remains bound to the
object's self, of type A'.
In a calculus like ours, with primitive objects but without primitive classes, this
mechanism provides a form of inheritance; it allows the reuse of methods of existing
objects in new objects. For example, consider a two-dimensional point p with a draw
8. SUBTYPING 109
method that produces a picture with the point in it. This point would have the type Pxyd
£ [x,y:Real, draw:Bitmap]. We may change the draw method in order to invert the color
of the picture, reusing the existing drawing code: p.draw;,(e:Bitmap)c;(s:Pxyd)invert(e).
For the sake of simplicity, we do not add elder to our core calculi. It is possible to
achieve roughly the effect of elder by using second-order techniques (3).
Then we can derive a contradiction. Let PosReal be the type of positive reals. Assume
PosReal <: Real and In : PosReal~Real (the natural logarithm). Define:
P £ [x:Real, fReal] [x=l.O,j=C;(s:P)ln(s.x)] : P is not derivable
Q £ [x:PosReal, fReal] we have Q <: P by (Sub Object/ covariant)
a £ [x=l.O,!=C;(s :Q)ln(s.x)] we have a : Q by (Val Object),
hence also a : P by (Val Subsumption)
b £ a.x:=-l.O from a : P we have b : P by (Val Update),
therefore bf : Real
Now we have ~ I- bf H In(-l.O) : Real; we have derived a typing for a program that
applies a function to an argument outside its domain. Hence the type system is
unsound as a direct consequence of adding (Sub Object/ covariant). The preceding
example can be recast with other nontrivial subtypings in place of PosReal <: Real, such
as a subtyping between object types. Note that the example involves simple field
update, and does not require proper method update.
The basic reason for this problem is that each method relies on the types of the
other methods, through the type of self. This dependence is essentially contravariant
and hence is incompatible with covariance. The problem disappears if we disallow cer-
tain updates; this solution is explored in the next section.
tion are not formally incorporated in our first-order calculi. We include variance in the
formal treatment of some of our calculi in Parts II and III.
By convention, any omitted 'U's are taken to be equal to 0. Therefore we obtain the
object types of Ob1<: as abbreviations.
The rule for object subtyping is the most affected by the addition of variance anno-
tations. This rule still says, to a first approximation, that a longer object type on the left
is a subtype of a shorter one on the right. Because of the variance annotations, however,
we use a new, auxiliary judgment E I- 'U B < : 'U' B' for inclusion of attributes with vari-
ance. The rule becomes:
Subtyping with variance annotations
(Sub Object) (Ii distinct)
E I- 'Ui Bi <: Vi' B/ 'v'iE l..n E I- Bi 'diE n+ l..n+m
(Val Select)
E f- a : [1i\li:Bi iel..n] \ljE (O, +) jE l..n
E I- a.lj: Bj
The rule (Val Object) is essentially unchanged since we add variance annotations only
to object types, not to objects.
In the typed calculi considered so far, we can find some typings for objects that use or
modify self. However, we cannot find satisfactory typings for objects whose methods
return either self or an updated self: this feature calls for the use of recursive types. For
example, it is natural to give a recursive type to an object with a method that returns
self; the result type of the method is the type of the object, so the type of the object must
be defined recursively.
In this chapter we discuss recursion in first-order calculi with and without subtyp-
ing, and we provide typings for the untyped examples of Section 6.5. The interaction
of subtyping and recursion is a delicate one. Considering our examples, we find that
we do not obtain all the subtypings we might like for recursive object types. We discuss
several remedies, including dynamic typing.
9.1 Recursion
To formulate the rules for recursive types, we need to introduce type variables, which
are typically denoted by X and Y. We also need to substitute types for type variables:
Notation
• The substitution of a type C for the free occurrences of X in B is written BHXf-q.
Similarly bHXf-q is the substitution of C for X in b.
• B{X} and b{X} are used to highlight that X may occur free in Band b, respec-
tively.
• Blq and bKq stand for BHXf-CB and bKXf-ct respectively, when X is clear
from context.
A recursive type Jl(X)B{X} is the unique solution of the equation X = B{X}, up to
isomorphism. The two types Jl(X)B{X} and BKJl.(X)B{X}» are therefore isomorphic. Their
isomorphism is given by two constructs, fold and unfold, that can be used explicitly to
move around the isomorphism. For example, if we want to define a type A isomorphic
to [/:A], we let A ~ Jl.(X)(I:X]. If a has type A then unfold(a) has type (I:A], and
fOld(A,unfold(a)) has type A and is equal to a; note, however, that a itself does not have
type (I:A].
In the literature (e.g., [18]), there is an alternative treatment of recursive types,
which we do not adopt. In that treatment, the two types Jl.(X)B{X} and BKJl.(X)B{X}B are
equal. This means, in particular, that the two types have the same sets of elements; it
also means that the two constructs fold and unfold are not necessary. This treatment of
recursive types is more concise than ours, but it has some technical disadvantages. For
example, it requires defining a notion of type equality. These technical disadvantages
are the main reason why we prefer to distinguish the types J.l.(X)B(X} and B(J.l.(X)B(X}t
and to use the constructs fold and unfold. In a programming language with recursive
types, the isomorphism is usually kept implicit for conciseness. This simplification can
be obtained easily even when the language is based on a calculus with explicit isomor-
phism, as we show in Chapter 12.
Formally, we have the following fragments for type variables and recursive types:
ll.x
(Env X) (Type X)
E f- <> Xtdom(E) E', X, E" f- <>
E, X f- <> E', X, E"f- X
(Type Rec)
E, Xf- A
E f- J.l.(X)A
(Val Fold) (where A'" Il(X)B{X)) (Val Unfold) (where A'" Il(X)B{X))
Ef- b: BlAB Ef-a:A
E f- fold(A,b) : A E f- unfold(a): BlAB
(Eq Fold) (where A '" Il(X)B{X)) (Eq Unfold) (where A'" Il(X)B{X))
E f- b H b' : BlAB Ef-aHa': A
E f- fold(A,b) H fold(A,b') : A E f- unfold(a) H unfold(a' ) : BHAB
(Eval Fold) (where A '" Il(X)B{X)) (Eval Unfold ) (where A'" Il(X)B{X))
Ef-a:A E f- b : BlAB
E f- fold(A,unfold(a» H a:A E f- unfold(fold(A,b» H b : BlAB
9. RECURSION 115
Consider, for example, the untyped self-returning object [1=C;(x)x) . To make this
object typable in ObIII' we insert a type annotation for the self variable and two fold
operations. With A ~ !l(X)[I:X), the rules yield that a ~ fold(A,[I=C;(x :[I :A)ifold(A,x))) has
type A, and that unfold(a).l is equal to a.
The translation of FI into ObI of Section 7.7 extends to a corresponding translation
of F1J1 into ObII'" The untyped A-calculus can be translated into FI~ the encodings of
untyped A-terms receive the recursive type !l(X)X~X . Combining these two transla-
tions, we obtain a translation of the untyped A-calculus into ObI,,:
Typed translation of the untyped A-calculus
A ~ !l(X)[arg:X, val:X) (globally free variables should be assigned type A)
UA ~ [arg:A, val:A) (the unfolding of A)
P E Var -7 ObI,,-term
~(x) ~ x
(p{y~a})(x) ~ ifx = y then a else p(x)
«x»p ~ p(x)
«b(a)>>p ~
(unfold(«b»p).arg;:c;(x:UA)<<a»p).val
«A(x)b»p ~
fold(A,
[arg=c;(x: UA)x.arg,
val=c;(x:UA)«b»Plx<-x.arg,))
We saw in Section 7.7 that, for each type A, recursively defined values !l(x:A)b of
type A are definable from objects; using the recursive type !l(X)X-7A, they are defin-
able from functions as well [67] :
!l(x:A)b{x} ~
(A(x: !l(X)X~A)b~ unfold(x )(x)>»
(fold(!l(X)X~A,(A(x:!l(X)X~A)b«unfold(x)(x)>»))
Therefore, recursively defined values can be encoded in any of ObIII' FIll' and FObII'"
ll<:x
(Env X<:) (TypeX<:) (Sub X)
E f- A X~dom(E) E', X<:A, En f- <> E', X<:A, E" I- <>
E, X<:A f- <> E', X<:A, E" f- X E', X<:A, E" f- X <: A
With type variables, we can revise and generalize the definition of variance given
in Section 8.1. We say that AIX} is covariant in X if B <: B' implies AlB» <: AlB'» for all B
and B'. We say that AIX} is contravariant in X if B <: B' implies ARB'» <: AHB» for all B
and B'. Otherwise, we say that AIX} is invariant in X.
The most interesting new rule in 1l<:11 is (Sub Rec). For establishing the subtyping
of two recursive types, ~(X)A <: ~(Y)B, the rule (Sub Rec) requires a subtype relation
between their bodies, A <: B, under the assumption of a subtype relation between their
recursion variables, X <: Y.
This rule determines the variance behavior of recursive types. If A is covariant in
X, then the variance of Z in I1(X)A is the same as the variance of Z in A. But if A is con-
travariant in X, then ~(X)A may be only invariant in Z, even if A is covariant or contra-
variant in Z. For example, ~(X)X~Z is invariant in Z.
The rule (Sub Rec) is part of a complete system for first-order recursive types, stud-
ied in (18). In that system the types ~(X)BIX) and BH~(X)BIXl» are equivalent. We do not
assume this equivalence; as a consequence, in the context of our calculi, rules stronger
than (Sub Rec) can be found, such as the following one:
(Sub Ree')
E f- ~(X)AIX) E f- ~(Y)BIY) E, X<:~(Y)BIY} f- AIX} <: BMY)BIYl»
E f- ~(X)AIX) <: ~(Y)BIY)
9. RECURSION 117
This rule is denotationally and operationally sound, and it implies (Sub Rec) as an
admissible rule. The standard (Sub Rec) is sufficient for almost all our purposes so, for
simplicity, we do not adopt (Sub Rec'). We leave as open questions all matters of com-
pleteness (in the sense of [18]).
The equational rules for recursion are not affected by subtyping. However, in the
presence of subtyping, some interesting rules can be derived. The following rule can
prove the equality of two fold terms even when the types used for folding are different:
(Eq Fold<: Lemmal) (where A '" J.l(X)B{XI, A' '" J.l(Y)B'{Y})
E I- A E I- A' E, Y<:Top, X<:Y I- B <: B' E I- b H b': BfA)
E I- fold(A,b) H fold(A',b') : A'
The hypotheses of this rule imply a subtype relation between the types used for fold-
ing: A <: A'. For the derivation of this rule, we use transitivity on the following inter-
mediate results: E I- fold(A,b) H fold(A,b'): A'; E I- fold(A,b') H fold(A',unfold(fold(A,b'»)
: A'; and E I- fold(A',unfold(fold(A,b'))) H fo1d(A',b'): A'. A further rule is derivable from
(Eq Fold<: Lemmal) and (Eq Fold):
(Eq Fold<: Lemma2) (where A'" J.l(X)B{XI, A' '" J.1{Y)B'{Y})
E I- A E I- A' E, Y<:Top, X<:Y I- B <: B' E I- b H b' : B'CA'» E I- b: BfAI
E I- fold(A,b) H fold(A',b') : A'
For the derivation of this rule, we use (Eq Fold<: Lemmal) with b =b' and (Eq Fold) for
E I- b H b': B'fA'}, and apply transitivity to the two results.
We now obtain the calculi:
Ob1<:/1 ~ Ob1<: u I::..<:x u 1::..<:/1
F1<:/1 ~ F1<: u ~:x U 1::..<:/1
FOb1<:/1 ~ FOb1<: u I::..<:x u 1::..<:/1
FOb1<:/1 is the strongest of these first-order systems, and Ob1<:/1 is a rather mild restric-
tion of it. We summarize the definition of FOb1<:/1 with the following syntax and scop-
ing rules:
Syntax of the FOb1<:/1 calculus
A,B::= types
X type variable
K ground type
Top the biggest type
[li:Bi iE l..n) object type (Ii distinct)
A-?B function type
I1(X)A recursive type
118 PART 1. UNTYPED AND FIRST-ORDER CALCULI
Ef-a:B
E f- jold(A,b) : A
The next four propositions are the analogues of Propositions 8.3-1 through 8.3-4.
Proposition 9.3-1 (rMinOb1<:Il typings are rObl<:Il typings)
If E f- a : A is derivable in rMinOb 1<:", then it is also derivable in rOb 1<:w
o
Proposition 9.3-2 (rMinOb1<:Il has unique types)
If E f- a: A and E f- a : A ' are derivable in rMinOb 1<:", then A '" A'.
o
Proposition 9.3-3 (rMinOb1<:Il has smaller types than rObl<:~
If E f- a : A is derivable in rOb 1<:", then E f- a : A ' is derivable in rMinOb 1<:Il for some
A' such that E f- A' <: A is derivable (in either system).
o
120 PART I. UNTYPED AND FIRST -ORDER CALCULI
(Red Unfold)
f- a -- fold(A,v)
f- unfold(a) -- v
In order to prove subject reduction, we extend the bound weakening and substitu-
tion lemmas of Section 8.3.2. The substitution lemma requires a notion of environment
substitution.
Environment substitution
EiYf-q for W.dom(E) is defined by:
I1S(Yf-q ~ ~
(E, x:A )(Yf-q ~ E(Yf-q, x:AIYf-q
(E, X<:AHYf-q ~ E( Yf-q, X<:A{Yf-C)
9.4 Examples
In this section we provide first-order typings for the examples of Section 6.5. All the
examples are revisited in Section 15.2.
We start with the example of objects with backup; the type in question is:
Bk ~ Il(X)[ retrieve:X, backup:X, ... J
We obtain the following typed version of the code, where UBk ~ [retrieve:Bk, backup:Bk,
... J is the unfolding of Bk:
fold(Bk,
[retrieve = C;(5j:UBk)fold(Bk, 51),
122 PART I. UNTYPED AND FIRST-ORDER CALCULI
J) : Bk
If we now consider the types Point ~ [x,y:Real) and PointBk ~ ~(X)[retrieve:X, backup:X,
x,y:Real), we obtain PointBk <: Point modulo an unfolding. Thus points with backup can
subsume points.
We use similar techniques to treat storage cells:
Cell ~ Il(X)[contents:Nat, get:Nat, set:Nat-+X)
UCell ~ [contents:Nat, get:Nat, set:Nat"-7Cell)
myCell : Cell ~
jold(Cell,
[contents = 0,
get = ~(s:UCell) s.contents,
set = ~(s:UCell) A(n:Nat)jold(Cell, s.contents := n)))
We can also write a typed version of the movable points:
P1 ! ~(X)[x:lnt, mv_x:lnt-+X) movable one-dimensional points
P2 ! Il(X)[x,y:lnt, mv_x,mv-y:lnt"-7X) movable two-dimensional points
origin) ~
jold(P 1,
[x=O,
mv_x = ~(s:(x:lnt, mv_x:lnt-+Pd) A(dx:lnt)jold(P j , S.x:= s.x+dx)))
and of the calculator:
Calc ! Il(X)[arg,acc:Real, enter:Real"-7X, add,sub:X, equals:Real)
The example of the numerals requires some additions to our first-order type sys-
tem. One possibility is second-order types, to be studied later. A simpler, first-order
alternative is sum types, if we are prepared to rephrase the example. We add a type
construction A+B, with the operations inlAB : A"-7(A+B), inr AB : B-+(A+B), and ifABC :
(A+B)"-7(A-+C)"-7(B"-7C)-+C. (From now on, we omit the subscripts.) Informally, A+B
is the disjoint union of A and B, inl and inr are the obvious injections, and if is used to
examine elements of A + B returning elements of C. For convenience, we also add a type
Unit with an element unit. The numerals can be expressed using sums as follows:
Nat ! ~(X)[case:Unit+X, succ:X]
zero!
fo1d(Nat,
[case = inl(unit),
succ = ~(x:[case:Unit+Nat, succ:NatJ)
jold(Nat, x.case := inr(jold(Nat, x»)))
9. RECURSION 123
Ob1<:11 with some ad hoc notion of Self. However, this approach would involve a fair
amount of "guessing" to find the right typing rules, which are not at all obvious.
Instead, we turn to second-order systems in Part II. We shall see that a plain second-
order extension of Ob1<:11 can already provide a concept of Self, essentially because
there we can express the requirement of being parametric in Self. After studying the
rules for Self derivable in second-order systems, in Chapter 16 we return to the prob-
lem ofaxiomatizing Self directly.
9.7.1 Typecase
The typecase construct evaluates a term to a result, and discriminates on the type of the
result. We write:
typecase a I (x:A)d 1 I d2
If a yields a result of type A, then (typecase a I (x:A)d 1 I d2) returns d1 with x replaced by
this result. If a yields a result that does not have type A, then (typecase a I (x :A)d1 I d2 )
returns d2•
The typing rule for the typecase construct is:
Typing rule for typecase
(Val Typecase)
E f- a: A' E, x:A f- d1 : D E f- d2 : D
E f- typecase a I (x:A)d 1 I d2 : D
This typing rule has, as first hypothesis, that a is well-typed; the precise type (A') is
irrelevant. The body of the first branch, dl, is typed under the assumption that x has
type A . The two branches have the same type, D, which is also the type of the whole
typecase expression.
It seems hard to write satisfactory equational rules for typecase. Moreover, in the
presence of typecase, the equational theory that we have developed so far is unsound
(in particular because typecase can distinguish the two terms that (Eq Sub Object)
equates). For these reasons, we consider only reduction rules for typecase.
In programming languages that include typecase, the type of a value is represented
using a tag attached to the value; typecase relies on this tag to perform run-time type
9. RECURSION 127
10.1 Syntax
The impc;-calculus is a pure, imperative calculus of objects:
Syntax of the imp<;-calculus
a,b ::= terms
x variable
[lj=C;(xj)b j jEl..n] object (Ij distinct)
a.1 method invocation
a . I~<;(x)b method update
clone(a) cloning
let x=a in b let
now imperative: it replaces the method named I in a with r,(y)b, and returns the modi-
fied object.
A cloning operation clone(a) produces a new object with the same labels as a, with
each component sharing the methods of the corresponding component of a. Cloning
may not be strictly necessary as a primitive, but it is convenient in examples, and it is
an interesting operation characteristic of prototype-based languages. Moreover, clon-
ing enables us to translate the r,-calculus into the impr,-calculus: we can express func-
tional method update as cloning followed by imperative method update.
The let construct evaluates a term, binds the result to a variable, then evaluates a
second term with that variable in scope. Sequential evaluation can be defined from let:
a; b g, let x=a in b for x~FV(b)
10.2 Fields
In the impr,-calculus every component of an object contains a method, as in the r,-cal-
culus; we have avoided introducing a separate notion of field components. Methods
are sufficient in functional calculi, where we can regard a field as a method that does
not use its self parameter, a field selection as a method invocation on the field, and a
field update as a method update on the field. So we could define:
field: [ ... ,l=b, ... ) g, [... , l=r,(x)b, ... J for x~FV(b)
field selection: 0.1 g, 0.1
field update: o.l:=b g, o.l~r,(x)b for x~FV(b)
These definitions have an unfortunate property: in both field definition and field
update, the implicit r,(x) binder suspends evaluation of the field until selection. This
semantics is both inefficient and inadequate for an imperative calculus, because at
every access the suspended fields are reevaluated and their side-effects are repeated.
Therefore we consider an alternative definition for fields, based on let. The let con-
struct gives us a way of controlling execution flow; this feature is used in translating a
calculus with eagerly evaluated fields, impr,f, into impr,.
Syntax of the impr,rcaIcuIus
a,b ::= terms
x variable
[li=b i ieJ..n, Ij=r,(xj)bj jen+J..n+"'J object (h Ij distinct)
a.l field selection / method invocation
a.l:=b field update
a.l~r,(x)b method update
clone(a) cloning
Here the li=bi are fields and the IFr,(Xj)bj are methods. Note that a field can be changed
into a method by a method update, and a method into a field by a field update.
10. UNTYPED IMPERATNE CALCULI 131
In imp~, the semantics of an object may depend on the order of its components,
because of side-effects in computing contents of fields. Therefore we set evaluation to
proceed from left to right, and we do not identify objects up to reordering of their com-
ponents. Usually, we write the field components before the method components.
We need not include sequencing and let in imp~ because they can be defined as
abbreviations:
let x=a in b{x} ~ [dej=a, val=~(x)b«x.defHJ . val
a; b ~ [jst=a, snd=bJ.snd
The translation of the calculus with fields is:
«clone(a)>> ~ clone(<<a»)
Thus we have a choice between an object calculus with fields, field selection, and
field update (imp9), and one with let (imp~). These calculi are inter-translatable. We
adopt imp~ as primitive because it is more economical and it enables easier compari-
sons with our other calculi.
10.3 Procedures
Just as the objects of Chapter 6 can express functions, the imperative objects of this
chapter can express procedures. In order to demonstrate this, we consider a call-by-
value A-calculus with side-effects, impA, that includes abstraction, application, and
assignment to A-bound variables.
Syntax of the impA-calculus
a,b ::= terms
x variable
x:=a assignment
A(x)b abstraction
b(a) application
132 PART I. UNTYPED AND FIRST-ORDER CALCULI
The intended semantics of this notation should be evident. For example, assuming that
we have arithmetic primitives, (A(X) X:=X+ 1; x)(3) is an impA-term whose evaluation
yields 4.
Within impA we can define abbreviations for local assignable variables and for
sequencing:
var x=a in b ~ (A.(x)b)(a)
a; b ~ (A(x)b)(a) with xaV(b)
We translate impA into the calculus with fields, imp9. Thus we show that both
imp9 and impc; can express procedures.
Translation of the impA.-calculus into the impC;r-calculus
P E Var ~ impC;f-term
j1J(x) ~ x
(p(Yf-a))(x) ~ ifx = y then a else p(x)
«x»p ~ p(x)
«x:=a»p ~ x.arg:=«a»p
«A(x)b»p ~ [arg=c;(x)x.arg, val=c;(x)«b»p{x<-x.arg,l
«b(a)>>p ~ (clone(«b»p).arg:=<<a»p).val
The translation of impA~ into impc;t is a trivial extension of the translation of impA
into impc;t. The impAc;r-calculus is fairly complete and convenient, and we use it in the
following examples.
10.4 Examples
Our first examples deal with the encoding of basic types, booleans, and natural num-
bers, from which we derive iteration constructs. The encoding of basic types is adapted
from previous chapters. As a programming example, we also present an implementa-
tion of the prime number sieve.
We use the notation:
let x=a;
for the top-level definition of an identifier that is used in later discussion and examples.
Each occurrence of this notation can be recast as an expression:
let x=a in b
where b is a collection of program fragments presented after the top-level definition.
We still use ~ for equality by definition. The new notation is necessary because of the
imperative nature of the examples in this chapter.
10.4.1 Booleans
The boo leans true and false are objects with three methods: if, then, and else. By indirec-
tion through self, true. if returns true. then, and false.if returns false.else.
134 PART 1. UNTYPED AND FIRST-ORDER CALCULI
let true = [if = ~(x) x.then, then = ~(x) x.then, else = ~(x) x.else);
let false = [if = r;(x) x.else, then = r;(x) x.then, else = r;(x) x.else);
ifb then e else d ~ «b.then ~ r;(x) e).else ~ r;(x) d).if x~FV(e)uFV(d)
The branch bodies e and d of the conditional are not immediately evaluated, since they
are contained inside r;-binders.
When a conditional is evaluated, the boolean b is modified in the components then
and else. This side-effect does not matter because the relevant branch is used immedi-
ately, and any subsequent use of b will reset it appropriately. If desired, the conditional
could be rewritten so as to clone b and to perform the side,-effect on the clone of b.
10.4.3 Iteration
Booleans and natural numbers can be used to define while and for iterators, respec-
tively:
10. UNTYPED IMPERATIVE CALCULI 135
while b do c ~
[I = ~(x) ifb then (c; x.I) else []] .I
for i in 1..n do c ~
[I = ~(x) A(i) case i when 0 do [J, when m+ 1 do (x.l(m); c; [])]./(n)
for x,m,tFV(c)
In the encoding of for, we could clone nand m to make sure that c can do no damage
by causing a side-effect on i. Both the while construct and the for construct return the
empty object.
We give an operational semantics that relates terms to results in stores. The oper-
ational semantics is based on a global store. We say that a term b reduces to a result v
to mean that, operationally, b yields v. Object terms reduce to object results consisting
of sequences of store locations, one location for each object component. In order to stay
close to standard implementation techniques, we avoid using formal substitutions dur-
ing reduction. We describe a semantics based on stacks and closures. A stack associates
variables with results; a closure is a pair of a method together with a stack that is used
for the reduction of the method body. A store maps locations to method closures.
The operational semantics is expressed in terms of a relation that relates a store 0,
a stack 5, a term b, a result v, and another store 0'. This relation is written:
0.5 I- b ..... v.o'
and it means that with the store 0 and the stack 5, the term b reduces to a result v, yield-
ing an updated store 0' in the process; the stack does not change.
1111-0 0, l-(~(x)b,5) I- 0
(Red x)
a.(5', x ....v, 5") I- 0
(Red Select)
a.5 I- a vo+ [/i=Li iE1..nj.a' a'(Lj) = (C;(xj)bj,5') xj~dom(5') jE l..n
a' .(5', Xj .... [li=Li iE1.."]) I- bj -- v.o"
(Red Update)
a.5 I- a -- [li=Li iEl""j.a' jE1..n LjEdom(a')
0.5 I- a.lj~c;(x)b -- [lFLi iE1.."j'(0'V-«C;(x)b,5})
(Red Let)
o.S I- a -- v' .0' a' .(5, x ....v') I- b -- v".a"
a.S I-Iet X=Q in b -- v".o"
The term reduction rules form the core of the semantics. A variable reduces to the
result it denotes in the current stack. An object reduces to a result consisting of a fresh
collection of locations; the store is extended to associate method closures to those loca-
tions. A selection operation reduces its object to a result, and activates the appropriate
method closure. An update operation reduces its object to a result, and updates the
appropriate store location with a new method closure. A cloning operation reduces its
object to a result; then it allocates a fresh collection of locations that are associated to
the existing method closures from the object. Finally, a let construct reduces to the
result of reducing its body in a stack extended with the bound variable and the result
of its associated term.
An algorithm for reduction can easily be extracted from these rules; it parallels
standard implementations of objects.
138 PART I. UNTYPED AND FIRST-ORDER CALCULI
Continuing the study of imperative calculi, we present a first-order type system with
subtyping for the untyped calculus impc; of Chapter 10. The subject reduction proof for
this system illustrates a basic proof technique for imperative operational semantics.
We give examples that demonstrate the relative simplicity of imperative typing: a
method may store results in global locations, but its result type will not reflect this.
When encoding classes, the imperative features enable side-effects at the class level and
global changes to class instances.
Our main concern in this chapter is to treat imperative semantics without distrac-
tions or unnecessary complications. Therefore we keep the type system to a minimum:
the only type constructor is the one for object types. Some of the type constructions we
have already studied could be integrated into the type system. In Chapter 17 we study
a more complete imperative calculus that, in particular, includes variance annotations
for distinguishing between updatable and non-updatable components.
11.1 Typing
Instead of introducing typing fragments one by one, as we have done for functional cal-
culi, we describe a whole typing system for imperative objects. Our typing rules for
imperative objects are similar to the ones for functional objects. They are, in fact, a
superset of the rules for objects in Ob1<:, except that terms do not contain type annota-
tions (to match our untyped operational semantics).
Typing rules
(Envj<i) (Env x)
E I- A x~dom(E)
fill- 0 E, x:A I- 0
The rules (Env 111) and (Env x) are used for building typing environments. The rules
(Type Object), (Sub Refl), (Sub Trans), and (Sub Object) concern the formation of types
and the subtype relation. An object type [li:Bi iE1..n] is a collection of method names Ii and
associated method result types Bi . A longer object type is a subtype of a shorter one,
without variation in the common type components. (This invariance is necessary for
soundness, as in previous chapters.) The remaining rules concern the typing of values.
There is one rule for each construct in the calculus; in addition, a subsumption rule con-
nects the value typing and subtyping judgments.
The rules for cloning and let are new, but straightforward. The rule for method
update is the same as in functional calculi; according to this rule, an update may mod-
ify an object whose complete set of methods is statically unknown.
let PI =
[x = 0, mv_x = C;(s) A(dx) s.x := s.x+dx);
let P2 =
[x = 0, y = 0,
mv_x = C;(s) A(dx) s.x := s.x+dx,
mv-y = C;(s) A(dy) s.y:= s.y+dy);
In the type system of Section 11.1, PI and P2 can be given the types:
PI £ [x:lnt, mv_x:lnt--7[]]
P2 £ [x,y:lnt, mv_x,mv-y:lnt--t[]]
where P2 is a subtype of PI. This result type [) is obtained by subsumption. The imper-
ative operational semantics produces the desired effect of moving a point without
requiring any particular result type for move methods. In contrast, an informative
result type is necessary in a functional framework.
Imperatively, there is no loss of type information when moving a point. For exam-
ple, suppose that f is defined with one-dimensional points in mind, with the type
PI--t[], and that norm2 is defined for two-dimensional points, with the type P2--tReal:
let f: PI--t[) = A(p) p.mv_x(I);
let norm2 : P2--tReal = A(p) sqrt(p.xI\2 + p.yI\2);
Because P2 is a subtype of PI, j{P2) is a legal call for P2 : P2. Therefore the following code
typechecks and, as expected, returns 1:
j{P2); norm2(P2)
Thus we have applied a procedure that expects a PI point to a P2 point, and after this
we are still able to use the point as a member of P2 • In contrast, in a functional setting
we may try to write norm2(j{p2)), which is not well-typed iff: P I --7[].
Even in an imperative setting, however, it is common to define methods that pro-
duce new objects, as opposed to modifying existing ones. If mv_x is to return a new
object, one must declare it with result type PI or P2., to take advantage of any change to
the x coordinate. One may try to redefine PI and P2 as recursive types (for example, PI
£ Jl(X)[x:lnt, mv_x:lnt--7XJ), but then P2 is not a subtype of PI. With this definition, all
the typing difficulties common in functional settings resurface.
11.2.2 A Calculator
We can write a typed imperative version of the calculator of Section 6.5.4, adding a
method for clearing the state:
Calc £ [arg,acc:Real, clear:[), enter:Real--t[), add,sub:[), equals:RealJ
144 PART I. UNTYPED AND FIRS[-ORDER CALCULI
Ie t calcula tor =
[arg = 0.0,
acc = 0.0,
clear = C;(s) s.arg := 0.0; s.acc:= 0.0; s.equals ., C;(s) s.arg,
enter = C;(s) A(n) s.arg := n,
add = C;(s) s.acc := s.equals; s.equals ., C;(s') s'.acc+s'.arg,
sub = C;(s) s.acc := s.equals; s.equals ., C;(s') s'.acc-s'.arg,
equals = C;(s) s.arg];
For example:
calculator.enter(S.O); calculator.add; calculator.equals = 10.0
As in the example of movable points, we do not need recursive types because of
the imperative semantics.
Suppose that, after c and c' have been created, and after instances of c and c' have
been allocated, we replace the pre-method II of c using a side-effect. The instances of c
reflect the change, because each method invocation goes back to c to fetch the pre-
method. The instances of c' also reflect the change, via the indirection through c'.
Therefore the effect of replacing a pre-method in a class is, by default, to modify
the behavior of all the instances of the class and of classes that inherited the pre-
method. This default is inhibited by independent updates to objects and to inheriting
classes.
Our definition of classes is tailored to obtain this global-change effect. If one is not
interested in global change, one can optimize the definition and remove some of the
run-time indirections. In particular, we can replace the proper method Ij=r,(z)c.lj in the
subclass c' with a field Ij=c.lj, so that a change to c.lj after the definition of c' will not
affect c' or instances of c'. Similarly, we can make c.new evaluate the pre-methods of c,
so that a change to c will not affect existing instances.
Combining these techniques, we obtain the following eager variants e and e' of c
andc':
let e =
[new=r,(z)let wI=z.11 in ... let wn=z.ln in [li=r,(S)wi(S) iEl..n),
li=r,(z)A(s)bi iEl..n);
let e' =
[new=r,(z)let wI=z.11 in ... let wn+m=z.ln+m in [li=r,(s)wi(s) iEl..n+m),
Ij=e.LjiE I..n,
Ik=r,(z)A(s)h kEn+I .. n+m);
In all cases, the typing of classes works just like in functional calculi (see Section
8.5), using variant function types. The typing of traits also works similarly, resulting in
a type-safe way to perform global change in object-based languages.
11.3.2 Examples
As an example, we reconsider the movable points of Section 11.2.1. Corresponding to
the object types PI and P2 we have the class types:
Class(P I ) == [new:P I , x:P r "'7Int, mVJ:PI ---7Int---7[))
Class(P2) == [new:Pz, x,Y:P2---7Int, mv_x,mv-Y:P2---7Int-7[)]
We can construct a class CPI for PI in the standard manner; because P2 is a subtype of
PI, a class CP2 for P2 may inherit pre-methods from CPI:
let CPI : Class(PI ) =
[new = r,(z)[ ... ),
x = r,(z) A(s) 0,
mv_x = r,(z) A(s) A(dx) s.x := s.x+dx);
146 PART I. UNTYPED AND FIRST-ORDER CALCULI
We define points PI and P2 by generating them from the classes CPI and CP2:
let PI = cpI·new;
let P2 = CP2·new;
Now we may update the class CPI; we change the mv_x pre-method so that it does not
set the x coordinate of a point to a negative number:
cpI.mv_x ~ <;(z) A(S) A.(dx) s.x := max(s.x+dx, 0)
The update is seen by PI because PI was generated from CPI; the update is seen also by
P2 because P2 was generated from CP2 which inherited mv_x from CPI:
PI.mv_x(-3).x 0
P2.mv_x(-3).x = 0
l:l::v:A
This means that the result v has type A with respect to the store type l:. The locations
contained in v are assigned types in l:.
Since in the operational semantics a result is interpreted in a store, we need to con-
nect stores and store types; we use a judgment to express that a store is compatible with
a store type:
l:l::o
Checking this judgment reduces to checking that the contents of every store loca-
tion has the type determined by the store type for that location. Since locations contain
closures, we need to determine when a closure has a method type. For this, it is suffi-
cient to check that a stack is compatible with an environment; the environment is then
used to type the method. We write:
l:I::S:E
to mean that the stack S is compatible with the environment E in l:.
Now, since stacks contain results and environments contain types, we can define
l: I:: S : Evia the result typing judgment, which we have already discussed.
Store typing
M ::= [1;:B; ;E1.."l~Bj method type (jEl..n)
l: ::= l;-M; ;E1.." store type (l; distinct)
l:l(l) ~ [1;:B; ;E1.."l if l:(l) = [1;:B; ;E1.."l~Bj
l:2(l) ~ Bj if l:(l) = [1;:B; ;El··"l~Bj
I:: ME Meth well-formed method type judgment
l:1::<> well-formed store type judgment
l:l::v:A result typing judgment
l:I::S:E stack typing judgment
l:l::o store typing judgment
(Method Type)
jEl..n
I:: [1;:B; ;El··"l=>Bj E Meth
(Result Object)
1: F 0 1:j (Li) == [li:~(Li) if!..n] "iIiE l..n
1: F [li=Li if!..n] : [li:1:2(Li) if!..n]
(Store Typing)
1: F 5i : Ei E;, Xi:1: j (li) I- bi : ~(li) "iIiE l..n
1: F li"'(~(Xi)bi,5i) if!..n
Note that the (Store Typing) rule types each closure with respect to the whole store,
accounting for cycles in the store.
o
The subject reduction theorem is:
Theorem 11.4-3 (Subject reduction)
If E I- a: A 1\ 0.5 I- a -- v.o t 1\ I: F 0 1\ dom(o) = dom(1:) 1\ 1: F 5 :E,
then there exist a type At and a store type 1:t such that
I:t ~ I: 1\ I:t F ot 1\ dom(ot) = dom(1:t) 1\ I:t F v : At 1\ At <: A.
Proof
We proceed by induction on the derivation of 0.5 I- a -- v.ot.
11. FIRST-ORDER IMPERATIVE CALCULI 149
Case (Red x)
o.(S', X..... [l,=li if1..n1, S") I-- <>
By induction hypothesis, since E f- a : [/j:Bj, ... ] 1\ 0.5 f- a vo+ [/i=Li iE1../I].o' 1\ :E 1= 01\
dom(o) = dom(:E) I\:E 1= 5 : E, there exist a type A' and a store type:E' such that:E' ~
:E I\:E' 1= 0' 1\ dom(o') = dom(:E') I\:E' 1= [/i=Li iEI../I] : A' 1\ A' <: [/j:Bj, ... ].
Since o'(Lj) = (q,(x)bj,S'), the judgment:E' 1= 0' must come via (Store Typing) from 1:'
1= 5' : Ej and Ej, Xj::E' \(Lj) f- bj : :E' 2(Lj) for some Ej . Since :E' 1= [li=Li iE 1../1) : A' must come
from (Result Object), we have A' == [li::E'2(Li) iE1..n] == :E'\(Lj)' Since A' <: [lj:Bj, ... ], we
have :E' 2(Lj) == Bj. Then from Ej, Xj::E' \(Lj) f- bj : :E' 2(Lj) we obtain Ej, xj:A' f- bj : Bj. More-
over, we get:E' 1= 5', xt·+[li=Li iE!..n] : Ej, xj:A' by the (Stack x Typing) rule.
Let E' == Ej, xj:A'. By induction hypothesis, since E' f- bj : Bj 1\ 0'.(5', xt...(li=Li iEl..n))
f- bi'- v.o" I\:E' 1= 0' 1\ dom(o') = dom(:E') I\:E' 1= 5', Xj"'+(/i=Li iEl..n) : E', there exist At
and :Et such that :Et ~ :E' 1\ :Et 1= 0" 1\ dom( 0") = dom(:Et) 1\ :Et 1= v : At 1\ At <: Bj.
We conclude:
• :Et ~:E by transitivity from:Et ~:E' and:E' ~:E.
• :Et 1= 0" with dom(o") = dom(:Et) .
• :Et 1= v : At with At <: A, by transitivity from At <: Bj and Bj <: A.
Case (Red Update)
0.5 f- a vo+ [/i=Li iE 1..11).0' jE l..n LjEdom(o')
0.5 f- a.lj:.=~(x)b vo+ (li=Li iE1..n).(o' .Lj ....(~(x)b,S»
12.1 Features
0-1 includes a number of characteristic object-oriented constructs, similar to those of
the pseudo-notation of the Review and to those of common object-oriented languages.
It includes some more exotic constructs as well. These constructs are suggested by our
calculi; they enrich 0-1 without significant complications in the underlying theory.
In the terminology of the Review, 0-1 includes both class-based and object-based
features, demonstrating that we can model both kinds of features, and even combine
them . In 0-1, objects have embedded methods, which can be updated. Classes can be
created by single inheritance from previously defined classes; methods of the super-
classes may be overridden and specialized. Because of a distinction between classes
and object types, 0-1 separates interfaces from implementations, and inheritance from
subtyping.
The type system of 0-1, though, is rather restrictive in its treatment of the type of
self. We work around this limitation by providing a typecase construct, in spite of the
drawbacks discussed in Section 2.5.
0-1 is not a synthesis of all the notations and techniques of Part I. We have selected
a set of features that are both interesting from a programming perspective, and suffi-
ciently easy to explain in terms of object calculi. However, other useful features could
be added without fundamental difficulty. For example, 0-1 does not provide any
mechanism for hiding the components of a class, but we could add such a mechanism
following the treatment in Section 8.5. Eventually we could turn the 0 - 1 core into a rich
and practical language, not too different from conventional object-oriented languages.
12.2 Syntax
The following table gives the syntax of 0-1 types:
Syntax of 0-1 types
A,B ::= types
X type variable
Top the biggest type
Object(X)[I{lJi:Bi i<1..n) object type (Ii distinct)
Class(A) class type
0-1 includes both object types and class types; in addition, Top is the biggest type.
For simplicity, 0-1 does not include standard basic types (such as the type of bool-
eans), or function types. Those types could be easily added or, as we have seen, they
could be encoded.
An object type is written in the form Object(X)[Iivi:BiIXI i<1..n), where X is a type
variable, each Ii is a label, each Vi is a variance annotation (-, 0, or +), and each B;\XI is a
type. An object type may be defined recursively: the variable X stands for the whole
object type. Each component l{IJi:BiIXI corresponds to an attribute, either a field or a
proper method, with type BiIXI. A subtype relation between object types supports sub-
sumption; the precise rules for subtyping are given in Section 12.4.
The variance annotation + identifies read-only attributes, while the variance anno-
tation 0 identifies read-write attributes. Proper methods are usually read-only
attributes and fields are usually read-write attributes, but this is not a requirement. We
also include the variance annotation - (write-only) because it is easy do so, but we do
not use it in examples. As discussed in Chapter 8, variance annotations are useful for
flexible subtyping and for protection, and also for certain encodings.
If A is an object type, then Class(A) is a class type; it is the type of classes for gen-
erating objects of type A. Classes and object types are different entities, so we distin-
guish between subtyping and inheritance. Furthermore, object types and class types do
not carry any implementation information, so we separate interfaces from implemen-
tations.
The syntax of terms is given below. We do not formalize the operational semantics
of these terms; it could be either functional or imperative. Previous chapters have
explored both kinds of semantics, and it is possible to adapt the techniques developed
there to 0-1. For an imperative semantics it is important to distinguish fields from
proper methods, because different evaluation orders are appropriate. Rather than com-
plicate the syntax and type rules of 0-1 with this distinction, we assume that attribute
names obey some convention that distinguishes field names from proper method
names. With the assumption that a suitable convention exists, the syntax can be read
either functionally or imperatively.
12. A FIRST-ORDER LANGUAGE 155
As the syntax of terms shows, 0-1 includes both object-based and class-based con-
structs. The two kinds of constructs coexist harmoniously. One could drop the object-
based constructs (object construction and method update); the result would be a lan-
guage expressive enough for traditional class-based programming. Alternatively, one
could drop the class-based construct (root class, subclass, new, and class selection),
obtaining an object-based language.
The construct for creating a single object is object(x:A) I;=bilx) iEl..n end. The result-
ing object has attributes li=bilx}' where x is the self variable for all the attributes.
Attributes can be fields or proper methods. The fields should not contain occurrences
of the self variable.
The construct for invoking a method is a.l; as a special case, this is also the construct
for field selection. There are two different constructs for update. The first one, a.1 := b,
updates a component to a field. The second one, a.1 := method(x:A) b end, updates a
component to a method.
The base class is root. All other classes are defined as subclasses of root. Given a
class c of a type C with methods named I; ;€l..n, the construct subclass of c:C with(x:A)
li=b;lx) i€n+l..n +moverride I;=b;lx) i€Ovr~!..n end introduces a new subclass. This subclass
extends the class c with the methods named Ii ;En+!..n+m and overrides the methods
named 1; iEOvr, where Ovr ~ Ln. The variable x is the self variable for all the code; it is
given the type A, which is the type of the objects generated from the subclass. Objects
generated from other classes may have type A too; in this sense, types are independent
from classes. However, if C is Class(A'), then A must be a subtype of A'; thus subclasses
correspond to subtypes.
Given a class c, the construct new c generates an object from c. The class selection
construct cA/(a) extracts the method named 1from c and applies it to a. This latest con-
156 PART I. UNTYPED AND FIRST-ORDER CALCULI
struct provides the functionality of super, since cl\l(x) may occur as part of the code for
a subclass of c with self variable x.
Classes, as well as objects, are first-class values. For example, in 0-1 an object may
have a class as one of its fields and this field can be updated. A parametric class can be
obtained simply as a function that returns a class.
Finally, 0-1 includes a typecase construct. The evaluation of typecase a when
(x:A)b 1 else b2 end causes the evaluation of a to some value v; if v has type A, then b1 is
evaluated with x bound to v; if v does not have type A, then b2 is evaluated instead. For
the reasons explained in Chapter 9, and as shown in the examples of Section 12.3, the
typecase construct plays a fairly important role in first-order languages such as 0-1.
As an abbreviation, we sometimes omit the variance annotation 0. In addition, we
have the following useful notations:
Root!
Class(Object(X)[])
class with(x:A) l;=b; ;,l..n end !
subclass of root:Root with(x:A) I;=b; ;,1..n override end
subclass of c:C with (x:A) ... super. I ... end g,
subclass of c:C with (x:A) ... cl\l(x) ... end
object(x:A) ... I copied from c ... end g,
object(x:A) ... l=cl\l(x) ... end
Root is the class type for the built-in class root, whose objects have type Object(X)[). A
subclass of root can be defined with the syntax class .. . end without explicit mention
of root. (We could take this construct as primitive, and then the subclass construct
would be definable from it and class selection.) Given a class c, super./ stands for cl\l(x}
in the scope of the definition of a subclass of c where x is the self variable. The notation
I copied from c is similarly derived from cl\l(x): it can be used in object-based style for
copying methods from classes directly into objects.
12.3 Examples
We write some small examples in 0-1. These examples are meant to illustrate the nota-
tion, and to help explain the type rules that are defined formally in Section 12.4. The
examples also illustrate the interplay between subtyping and typecase.
In order to write the examples, we assume basic types (Bool, Int) and function types
(A~B, contravariant in A and covariant in B). We use the notation fun(x:A) b end for a
function with argument x of type A and body b. Since we have not endowed 0-1 with
definition constructs, we use an informal top-level notation to this effect.
We begin by defining two object types:
Point g, Object(X)[x: Int, eq+: X~Bool, mv+: Int~Xl
The type Point has three components: an integer coordinate x; a binary method eq,
which compares self with another point and returns a boolean; and a method mv,
which takes an integer and returns a point. The type CPoint adds a color c. When the
type variable X occurs in Point, it stands for Point; in CPoint, it stands for CPoint.
According to the rules of Section 12.4, the definitions yield CPoint <: Point, because
CPoint is longer than Point, and the occurrences of X in Point are matched with occur-
rences of Point in CPoint. Our examples show that subtypings such as this one are desir-
able in programming; many object-oriented languages allow them. The type of mv in
CPoint is lnt--"Point; later we explore the effect of changing it to lnt--"X. The type eq in
CPoint is Point--"Bool; if we were to change it to X--"Bool we would lose the subtyping
CPoint <: Point.
Corresponding to the two types Point and CPoint, we have two classes:
pointClass : Class(Point) ~
class with (self: Point)
x=O,
eq = fun(other: Point) self.x = other.x end,
mv = fun(dx : Int) self.x := self.x+dx end
end
cPointClass : Class(CPoint) ~
subclass of pointClass: Class(Point)
with (self: CPoint)
c = black
override
eq = fun(other: Point)
typecase other
when (other': CPoint) super.eq(other') and selfc = other'.c
elsefalse
end
end
end
The class cPointClass inherits x and mv from its superclass pointClass. Although it could
inherit eq as well, cPointClass overrides this method as follows. The definition of Point
requires that eq work with any argument other of type Point. In the eq code for cPoint-
Class, the typecase on other determines whether other has a color. If so, eq works as in
pointClass and in addition tests the color of other. If not, eq returns false.
We can use cPointClass to create color points of type CPoint:
cPoint: CPoint ~ new cPointClass
Calls to mv lose the color information. In order to access the color of a point after it has
been moved, a typecase is necessary:
158 PART I. UNTYPED AND FIRST -ORDER CALCULI
movedColor : Color !
typecase cPoint.mv(1)
when (cp: CPoint) cp.c
else black
end
As this code fragment suggests, we may want to define a stronger type of color
points that would preserve type information on move. We can do this by changing the
type of mv to Int~X, where X stands for the whole object type being defined. More pre-
cisely, we write:
CPoint2 ! Object(X)[x: Int, c: Color, eq+: Point~Bool, mv+: Int~X)
The subtyping CPoint2 <: Point follows from the rules of Section 12.4. The soundness of
this subtyping is nontrivial and depends on the read-only annotation on mv.
Corresponding to this new object type, we have a new subclass of pointClass:
cPointClass2 : Class(CPoint2) !
subclass of pointClass: Class(Point)
with (self CPoint2)
c = black
override
eq = fun(other: Point)
typecase other
when (other': CPoint2) super.eq(other') and selfc = other'.c
else false
end
end,
mv = fun(dx: Int)
typecase super.mv(dx)
when (res : CPoint2) res
else ... (error)
end
end
end
This subclass overrides eq and mv. Again the override of eq is optional. Instead, the
override of mv is now necessary. The mv method of pointClass does not have an appro-
priate type for cPointClass2: the type definitions say only that it has type Int~Point
rather than Int~CPoint2 as cPointClass2 requires.
Quite incidentally, the code for mv in the superclass could be given the type
Int~CPoint2, but this fact cannot be known without looking at the implementation of
pointClass. To obtain such knowledge, a compiler would have to violate the separation
12. A FIRST-ORDER LANGUAGE 159
12.4 Typing
We formalize the type rules of 0-1 in our usual format. The rules are based on the fol-
lowing judgments:
Judgments
0-0 environment E is well-formed
EI-A A is a well-formed type in E
E I- A <: B A is a subtype of B in E
E I- vA <: v'B A is a subtype of B in E, with variance annotations v and v'
El-a:A a has type A in E
In the rules for types, the main innovation is that Class(A) is a type when A is an
object type:
160 PART I. UNTYPED AND FIRSf-ORDER CALCULI
Types
(Type X) (Type Top)
E', X<:A, En I- 0 EI-o
E', X<:A, En I- X E I- Top
(Type Object) (Ii distinct, ViE1°,-,+}) (Type Class) (where A'" Object(X)[Iivi:BiI XI iEI."))
E, X<:Top I- Bi ViEl..n EI-A
E I- Object(X)[liUi:Bi i.1..II) E I- Class(A)
(Sub Object) (where A", Object(X)[ltwBiI XI iEI.."+/n), A' '" Object(X')[/,v;':B;'IX', i'I.."))
E I- A E I- A' E, X< :A ' I- Ui BilX} <: \)i' B;'(A'} V iEl..n
E I- A <: A '
The most interesting rule is (Sub Object), which deals with subtypings between
object types with variance annotations. It is more complex than the analogous rules
that we have considered so far because of the occurrence of a bound variable X in the
construct Object(X)[ ... ). When testing A <: A', component by component, the occur-
rences of X' in A ' are replaced with A'; for the occurrences of X in A, there is the
assumption that X <: A '. As in Chapter 8, three rules complement (Sub Object) for prov-
ing subtypings with variance annotations. In the example of Section 12.3, CPoint2 <:
Point is establishing by checking that + int---)X <: + int--7Point under the assumption X
< : Point.
The remaining rules for subtyping are basic ones (such as that for reflexivity). Note
in particular that there is no rule for subtyping class types.
Finally, 0-1 has the following rules for terms:
12. A FIRSf-ORDER LANGUAGE 161
Terms
(VaISubsurnption) (Val x)
E I- a : A E I- A <: B E', x:A, En f- 0
(Val New)
E I- c :Class(A)
E I- new c: A
(Val Root)
EI-o
E I- root: Class(Object(X)[])
(Val Subclass) =
(where A Object(X)[l,'Uj:Bj/X) jEl..n+m], A' "" Object(X')[I,'U;':Bj'/X'} ;Et ..n],
Ovr~l ..n)
(Val Typecase)
E f- a: A' E, x:A f- bl : D E f- b2 : D
E f- typecase a when (x:A)b l else b2 end: D
The rules (Val x) and (Val Subsumption) are standard. The rule (Val Typecase) is
as in Section 9.7. For constructing directly objects of type A == Object(X)[I;'Ui:Bi/X) iE1..nj,
according to (Val Object), it suffices to provide attributes of types BilAB, with the recur-
sion variable X replaced with the entire object type A; the self variable is given type A.
Similar assumptions are required for the rules (Val Update) and (Val Method Update).
In the rule for selection, (Val Select), a component Ij of an object of type A ==
Object(X)[liui:Bi/X) iE1..nj is selected; the resulting type is Bj{AB, where the recursion
variable X is replaced with the type A.
As for classes, the rules (Val New) and (Val Root) are straightforward. The rule
(Val Class Select) is also simple; it says that if a class c is for a type Object(X)[liui:Bi\X)
ie1..nj then cAlj behaves as a function from Object(X)[I;'Ui:B;lX) id"nj to Bj .
The rule for constructing subclasses, (Val Subclass), is fairly delicate. It requires
that the subclass and the class are for object types A and A', respectively, and that A <:
A'; therefore subclassing requires subtyping. But this is not all; additional conditions
are necessary for inheritability: a subclass of a class c' may inherit a method Ii only
under certain sub typing assumptions (in the rule, E f- B;'{A'B <: BifAJ). These assump-
tions guarantee that a method of the superclass behaves soundly when inherited in the
subclass. For the methods that c' does not inherit (the new ones and the overridden
ones), the rule stipulates simply that they must have the types expected in the subclass
(in the rule, E, x:A f- bi : Bi{AB).
In short, if a subclass redefines the type of a method, it is forced to override the
method. We choose this subclassing mechanism for its flexibility. It would be possible
to restrict the rule (Val Subclass) to guarantee that methods are always inheritable. The
issue of inheritability will receive a different treatment in Parts II and III.
12.5 Translation
We relate 0-1 to our calculi via a translation. In general, such a translation would have
to be defined by induction on the typing derivations of the source language. For sim-
plicity, instead, we give a more direct description of the translation: we add some type
information to terms, and proceed by induction on the syntax of the source language.
Specifically, we only need to add type information to field update, adopting the syntax
(a :A).lj := b (instead of a.lj := b) and the rule:
12. A FIRST-ORDER LANGUAGE 163
«x» ~ X
«Top» ~ Top
«Object(X)[ljWBj jE1..n]» ~ J.l(X)[ljW«B j» iEl.n)
«Class(A)>> ~ [new+:«A», It:«A»-t«BiH«A»B iE1..n]
where A == Object(X)[/iui:B;{Xj jE1..n]
«~» ~ ~
«E, X<:A» ~ «E», X<:«A»
«E, x:A» ~ «E», x:«A»
The translation of terms is virtually forced by the translation of types, but is not
altogether simple. We write «a» for the translation of a. The precise definition of «a»
relies on many uses offold and unfold, necessary in connection with recursive types; it
is easiest to ignore them at first. It is also convenient to use our notations for fields in
object calculi, for example writing [.. . , l=b, ... ] instead of [ . . ., 1=C;(y:A)b, ... ] for an
164 PART I. UNTYPED AND FIRST-ORDER CALCULI
«x» ! x
«object(x:A) li=bi iE1..n end» ! [li=C;(x:«A»)«M iE1..n]
«a.l» ! «a».1
«(a:A).1:= b» ! «a».l:=«b»
«a.l := method(x:A) bend» ! «a».l~c;(x:«A»)«b»
«new c» ! «c».new
«root» ! [new=[]]
«subclass of c':Class(A') with(x:A) IFbi iEn+1..n+m override li=b i iEOvr end» !
[new=c;(z:«Class(A)>> )[li=C;(s:«A» )z.li(s) iE I..n+m],
li=«c'».li iE1..n-Ovr,
li=A.(x:«A» )«bi» iEOvrvn+ I..n+m]
«c"l(a)>> ! «C».l(<<a»)
«typecase a when (x:A)bt else b2 end» ! typecase «a» I (x:«A»)«bt» I «b2»
The preliminary translation deals trivially with typecase and with the object-based
constructs of 0-1. More interestingly, it maps a class to a collection of pre-methods
plus a new method. For the base class root, there are no pre-methods. For a class sub-
class of c' .. . end, the collection of pre-methods consists of the pre-methods of c' that
are not overridden, plus all the pre-methods given explicitly. The new method assem-
bles the pre-methods into an object; the construct new c is interpreted as an invocation
of the new method of «c». The construct «c"l(a)>> is interpreted as the extraction and the
application of a pre-method.
These ideas lead to the precise definition of the translation. In this definition, we
write Ct for BRq when C is a recursive type J.1(X)B{X}.
Translation of 0-1 terms
«x» ! x
«object(x:A) li=bdx} iE1..n end» ! fo1d(«A»,[li=C;(x :«A»t)<<M(jold(<<A»,x)>> iE1..n])
where A=' Object(X)[Ii\Jj:Bj jE1..n]
«a.l» ! unfold(<<a»).1
«(a:A).I:= b» ! fold(«A»,unfold(«a»).l~c;(x:<<A»t)<<b»)
where A =' Object(X)[Ii\Jj:Bj jE1..n] and xaV(<<b»)
12. A FIRST-ORDER LANGUAGE 165
Part II is primarily concerned with second-order concepts: type quantification and the
type Self. In this chapter, we start by adding second-order type quantifiers to our first-
order calculi. These quantifiers are standard; they were not designed specifically with
objects in mind. However, these quantifiers can be combined with recursive types to
produce an interesting new construct that is recognizable as formalizing the type Self.
The interaction of Self with object types is the subject of Chapter 15. Subsequent chap-
ters concern the direct axiomatization of Self types, and their use in a programming
language.
In this chapter, we first introduce universal quantifiers and existential quantifiers.
From existential quantifiers and recursion we define the Self quantifier. For the purpose
of defining the Self quantifier, only existential quantifiers and recursion are needed;
one could dispense with universal quantifiers. For the purposes of object-oriented lan-
guages, only the Self quantifier is needed; one could dispense with most of the second-
order baggage. However, universal quantifiers are still useful in an object-oriented lan-
guage to provide parametric polymorphism, and existential quantifiers to provide data
abstraction.
(EqFun2) (EqAppI2)
E, X I- b H b' : B E I- b H b' : V(X)B{X} E I- A
E I- A(X)b H A(X)b' : V(X)B E I- b(A) H b'(A) : BiAB
The rule (Type All) forms a quantified type V(X)B in E, provided that B is well-
formed in E extended with X. The rule (Val Fun2) constructs a type abstraction A(X)b
of type V(X)B, provided that the body b has type B for an arbitrary type parameter X
(which may occur in b and B). The rule (Val Appl2) applies such a type abstraction to a
typeA.
The equational rules (Eq Fun2) and (Eq Appl2) are simply congruence rules. The
rules (Eval Beta2) and (Eval Eta2) are the equational versions of ~-reduction and 1]-
reduction for type abstractions.
The second-order A-calculus F'U usually called F [63], is assembled below, along
with related calculi. The ground type K used in first-order calculi is left out because free
algebras can be encoded within F [28].
Starting in Section 13.2, we include existential quantifiers in all our second-order
calculi. We indicate calculi without existentials by italic letters:
F ! l:i x U l:i-.. U l:ix U l:iv ! Ful:i 11
Ob ! l:i x U l:iob U 6.x U 6.'1 ! Obul:i11
FOb ! Ful:iob ! FOb ul:i 11
The encoding of function types as object types (Section 7.7) extends trivially to sec-
ond-order calculi, so Ob can encode F and FOb. It is evident that F cannot encode Ob,
13. SECOND-ORDER CALCULI 171
since F is strongly normalizing [63) and Ob is not (Section 7.4.1). However, F" can
encode Ob and Ob" via a typed version of the self-application semantics (Section 6.7.1).
"'<:v
(TypeAIk:) (Sub All)
E, X<:A f- B Ef-A'<:A E,X<:A'f-B<:B'
E f- 'V(X<:A)B E f- 'V(X< :A)B < : 'V(X<:A')B'
These rules generalize those of the fragment "'v to bounded universal quantifiers.
The rule (Sub All) describes the subtype relation between the new quantifiers; note the
inverse inclusion of the bounds, and the assumption under which the bodies are com-
pared. This rule determines the variance behavior of the universal quantifier. If A is
contravariant in Yand B is covariant in Y, then 'V(X< :A)B is covariant in Y. If A is cova-
riant in Yand B is contravariant in Y, then 'V(X<:A)B is contravariant in Y. Therefore
we say that a universally quantified type is contravariant in its bound and covariant in
its body.
The equational rules are simple generalizations of those of the fragment d=v.
~:3
(Val Pack<:)
E f- C <: A E f-bgq : Bgq
E f- pack X<:A=C with b{X}:B{X} : 3(X<:A)B{X}
(Val Open<:)
E f- C : 3(X<:A)B E f- D E, X<:A, x:B f- d : D
E f- open cas X<:A,x:B in d:D: D
174 PART II. SECOND-ORDER CALCULI
The rule (Sub Exists) determines the variance behavior of the existential quantifier.
If A and B are covariant in Y, then so is 3(X<:A)B. If A and B are contravariant in Y, then
so is 3(X<:A)B. Therefore we say that an existentially quantified type is covariant in its
bound and its body.
If B is covariant in X, then 3(X<:A)B{X) is isomorphic to BlAB; the isomorphism can
be shown formally for particular systems [44, 1041. If B is not covariant in X, then
3(X<:A)B{X) may not be isomorphic to BlAB. In particular, 3(X<:A)[lj:Bj{X) j.I..") is not
isomorphic to [lj:Bj(A} j.1.."), even if each Bj is covariant in X, because there is no appro-
priate function from 3(X<:A)[lj:B;lX) j.I..") to [lj:BjIA} j.I.."). For instance, [1:X) is not
covariant in X, and 3(X<:Int)[l:X) is not isomorphic to [l:Int). The failure of this isomor-
phism is fairly clear since values of types 3(X<:Int)[l:X) and [l:Int) cannot be used in the
same ways; for example, if 0 has type [l:Int) then it is legal to set 0.1:=3, but there is no
corresponding operation on values of type 3(X<:Int)[I:X).
The fragment ~=<;3 gives an equational theory for bounded existentials.
~=<;3
(Eq Pack<:)
E f- C <: A' E f- A' <: A E, X<:A' f- B'{X) <: B{X) E f- blC) H b'(q : B'(q
E f- pack X<:A=C with b{X):B{X) H pack X<:A'=C with b'{X):B'{X) : 3(X<:A)B{X)
(Eq Open<:)
E f- c H c': 3(X<:A)B E f- D E, X<:A, x:B f- d H d' : D
E f- open cas X<:A,x:B in d:D H open c' as X<:A,x:B in d':D : D
(Eval Repack<:)
E f- b : 3(X<:A)B{X) E, y:3(X<:A)B{X) f- d{y) : D
E f- open bas X<:A,x:B{X) in d(pack X'<:A;X with x:BIX'}»:D H dlb) : D
The rules (Eq Pack<:) and (Eq Open<:) are congruence rules. The rule (Eq Pack<:)
takes subtyping into account, equating pack terms with different type bounds A and A'
and type bodies B{X) and B'{X) . A more flexible congruence rule is derivable:
(Eq Pack<: Lemma)
E I- C <: A' E I- A' <: A E, X<:A' I- B'{X) <: B{X)
E f- blq H b'(q : B(q E I- b'lq : B'(q
E f- pack X<:A=C with b{X):B{X) H pack X<:A'=C with b'{X) :B'{XI : 3(X<:A)B{X)
13. ::iECOND-URDER LALCUU
=
For the derivation of (Eq Pack<: Lemma), we use (Eq Pack<:) twice, first with b b' and
then with A = A' and B = B', and apply transitivity to the two results.
The rules (Eval Unpack<:) and (Eval Repack<:) are evaluation rules. The rule (Eval
Unpack<:) requires c to contain the types A and B mentioned in the open construct. This
may not be the case if c has been obtained via subsumption: c could have the form (pack
X<:A'=C with b:B') where A' <: A and B' <: B. However, then, the rules (Eq Pack<:) and
(Eq Open<:) can be used to replace the inadequate c with an adequate one.
The equational rules are somewhat conservative. In particular, they do not equate
pack terms based on different representation types. For example, we might expect that
the terms (pack X< :Int=Nat with (O,SUCCNat» and (pack X<:Int=Int with (O,SUCClnt» be equal
at type 3(X<:Int)Xx(X ~X) because they have the same behavior, but this does not seem
to be derivable. This equality can be proved in a logic that includes an axiom of para-
metricity [104] . For simplicity we restrict attention to our conservative rules, although
more ambitious rules may have advantages in combination with objects.
Within F<: (and hence FOb<,) it is possible to encode bounded existential quantifi-
ers with the following definitions:
3(X<:A)B{X} g, (Y not occurring in A or B)
"tI(Y<:Top )("tI(X<:A)B{XH Yh Y
pack X<:A=C with b{X):B{X} g, A(Y<:Top)A(f"tl(X<:A)B{XH Y)j(C)(bgq)
open cas X<:A,x:B{X} in d{X,x} :D g, c(D)(A(X<:A)A(X:B{X})d{X,x})
All our rules for 3, except for (Eval Repack<:), are derivable from the encoding. (In par-
ticular, the encoding of (Eq Pack<:) is derivable in F<: using the Domain Restriction
Lemma of [44].) We take 3 as primitive in order to have (Eval Repack<:), which is useful
in the sequel. The primitive 3 is also important as an addition to the calculi Ob<: and
Ob<:w These calculi can express only invariant function types (as far as we know), so
the encoding of 3 in terms of"tl and ~ just shown does not validate (Sub Exists) in Ob<:
and Ob<:w
We now add 3 to our calculi. The fragments for the unbounded existential quanti-
fier, ~3 and ~=31 are in the Appendices A.2 and A.3. The definition of F<:fl is repeated in
Appendix B.2.
F g, FV~3 Ffl g, FflV~3
Ob g, ObV~3 Obfl g, ObflV~3
FOb g, FObv~3 FOb fl g, FObflV~3
In Section 13.4 we show that Ob<: can encode F<:1 and that Ob<:fl can encode F<:w
Conversely, we discuss several more or less satisfactory interpretations of Ob<: in F<:fl
in Chapter 18.
176 PART II. SECOND-ORDER CALCULI
We summarize the definition of FOb q " the largest of the calculi defined above, by
giving its syntax and scoping rules:
Syntax of the FOb<:J! calculus
A,B,C,D ::= types
X type variable
Top the biggest type
[/i:Bi iE!..n] object type (I i distinct)
A-?B function type
J.1(X)A recursive type
V(X<:A)B bounded universal type
3(X<:A)B bounded existential type
a,b,c,d ::= terms
x variable
[/i=~(Xi:Ai)bi iE!..n] object (Ii distinct)
a.1 method invocation
aJ:;.~(x:A)b . method update
A(x:A)b function
b(a) application
fold(A,a) recursive fold
unfold(a) recursive unfold
A(X<:A)b type abstraction
b(A) type application
pack X< :A=C with b:B data abstraction packaging
open cas X< :A,x:B in d:D data abstraction opening
FV(x) ~ {x}
FV([li=~(Xi:Ai)bi iE!..n]) ~ v iE!..n FV(~(Xi:Ai)bi)
FV(a.l) ~ FV(a)
13. SECOND-ORDER CALCULI 177
Notation
• We write A{X+, Y-) to mean that both A{X+} and A{Y-} .
• We may use A{X+) as a type, and then it stands for A together with the assertion
A{X+). We may similarly use A{X+, Y-}, A{X-}, and A{XO} as types. For example,
E I-a: B'{X-)~B"{X+) means that EI-a: B'~B" and thatthe assertions B'{X-} and
B"{X+} hold.
The following lemmas establish that positive occurrence is a sufficient condition
for covariance, and negative occurrence for contravariance. They are proved in Appen-
dix c.l.
Lemma 13.3-1
Assume E'{Y+,X-} and B{X+).
If E, X<:A, E'{X,XB f- B{X}, then E, X<:A, E'(X,At I- B{X} <: BIAI.
o
Lemma 13.3-2
Assume B{Y+,X-).
If E, X<:A f- BIX,XB and E I- Cl <: C2 and E I- C2 <: A, then E I- B{C1,C2& <: B{C2,C1B.
o
chapter we show how the Self quantifier interacts with object types, and how it helps
in representing the type Self discussed in Sections 2.8 and 9.6.
s.get:=n) : st as the body of the set method . Using this method body, we can build an
object of type st(st). Therefore we have:
st: st ~ (st, [get = 0, set = ~(s:st(st)) t..(n:Nat) (st, s.get:=n)])
Now the following rules for the Self quantifier can be derived from the rules for Jl
and for 3.
182 PART II. SECOND-0RDER CALCULI
Notation
• wrap(A,c) stands for wrap(X<:A=A)c when X does not occur in c.
• wrap(X=A)c(XI stands for wrap(X<:A=A)c(XI.
Note the important rule of covariance, (Sub Self). This rule holds because existen-
tial types are covariant in their bounds, and because recursion in Il(Y)3(X<:Y)B(XI
involves only a single covariant position. This rule can be verified as follows:
E, X f- B <: B' antecedent
E, Z, Y<:Z, X<:Y f- B <: B' by weakening lemmas, for fresh Y,Z
E, Z, Y<:Z f- 3(X<:Y)B <: 3(X<:Z)B' by (Sub Exists)
E f- Il(Y)3(X<:Y)B <: Il(Z)3(X<:Z)B' by (Sub Rec)
13. SECOND-ORDER CALCULI 183
The other rules are roughly analogous to those for f.1 and for 3. The derivation of
(Eq Wrap) relies on (Eq Fold<: Lemma2) of Section 9.2. Much as we have done for the
(Eq Pack<:) rule for 3, we derive a flexible congruence rule from (Eq Wrap):
(Eq Wrap Lemma) (where A '" ~(X)B{XI, A' '" 9X)B'{XJ)
E I- C <: A' E I- A E, Y I- B'i¥) <: B{Y&
E I- bKCa H b'KCa : Bie» E I- b'IC& : B'ie»
E I- wrap(Y<:A=C)b{Y) H wrap(Y<:A'=C)b'{Y) : A
The storage cell definition of Section 13.5.1 can now be understood formally. We
rewrite the example taking advantage of the new notation for wrapping:
St ! ~(Self)[get: Nat, set:Nat~Selfl
st: St ! wrap(Self=St)[get = 0, set = C;(s:St(Self)) "A(n:Nat) wrap(Self, s.get:=n)]
Later examples frequently adopt this choice of Self as a name for a variable bound by
the Self quantifier.
This calculus departs from second-order "A-calculi by omitting function types and the
standard quantifiers. It seems that, in <;Ob, the only function types that can be encoded
are invariant, and that the standard second-order quantifiers are not expressible. Still,
184 PART II. SECOND-ORDER CALCULI
e1 rl
W D W
e2 r2
(D~D) D (D --1 D)
e3 r3
(L~ D).l D (L ~ D).l
Thus, omitting e's and r's, Pi+1(*) is *; Pi+l(J)(X) is Pi(f(Pi(X))); and Pi+1(o)(mj) is
pi(o(mj)) if j $ i and .1 otherwise.
14. ASEMANTICS 187
If xED is such that Pn(x) = x for some n, then x is finite, and the least n for which
pn(X) = x is the rank of x. If x is finite and (Yj) is an increasing sequence, then x b Uj Yj
implies that x b Yk for some k.
We commonly view W, (D-7D), and (L-7D) as subsets of D, and omit the various
e's and r's. We may write * for el(*), x E (D-7D) for x E e2(D-7D), and x E (L-7D) for x
E e3(L-7D). When x, Y E D and mEL, we write x(y) for T2(X)(Y); similarly, we write x(m)
for r3(x)(m) if r3(x) E (L-7D) and for.L if T3(X) = .L. Further, we use A-notation for ele-
ments of (D-7D); and we write ((ml=xl, ... , mn=xn» for the function in (L-7D) that maps
ml to Xl, . . . , mn to Xn, and maps all other labels to *. When fis a function, we write f(lf-x)
for the function that maps I to x and is identical to f elsewhere.
14.2.1 Preliminaries
When we discuss binary relations over D, by convention we always mean binary rela-
tions that do not have * in their domains. It is easy to show that all our constructions
preserve this property.
A per (partial equivalence relation) is a symmetric, transitive, binary relation on D.
A binary relation P is uniform if x P Y implies (pj(x» P (pj(Y» for all i. It is closed under
limits of increasing sequences if, whenever (Xj) and (Yj) are increasing sequences and Xj
P Yj for all i, then (Uj Xi) P (Uj yj). It is complete if it is closed under limits of increasing
sequences and .LP.L.
A cuper is a complete uniform per. The set of all cupers is CUPER. Below, all types
are interpreted as pers in CUPER. Throughout, the variables R, S, and T range over
CUPER, and P and Q range over arbitrary binary relations.
The set of all cupers with this distance function is a complete metric space (in fact,
an ultrametric space). In this complete metric space, F is a nonexpansive function if dis-
tance(F(R),F(T» ~ distance(R,T) for all Rand T; that is, F maps any two cupers that are
equal up to rank r to two cupers that are equal up to rank r. Similarly, F is a contractive
function if distance(F(R),F(T» ~ distance(R,T)/2 for all Rand T; that is, F maps any two
cupers that are equal up to rank r to two cupers that are equal up to rank r+ 1. Amadio
[17] has verified that the usual type constructors ~ and Jl are contractive or nonexpan-
sive on cupers as on ideals [82]; in Section 14.2.5 we review some of Amadio's results
and prove other similar ones.
By the Banach Fixpoint Theorem, if F is a contractive function, then it has a unique
fixpoint; this gives rise to an interpretation of recursive types as follows. If F is a con-
tractive function, then for any 5 the sequence 5, F(S), .. . ,Fn(S), ... converges; moreover,
the distances between consecutive elements get uniformly smaller:
distance(F"(S),F"+I(S» ~ distance(S,F(S»/2" ~ 1/2"+1
Therefore, the sequence has a limit F~. This limit is the unique fixpoint of F (indepen-
dently of the choice of 5):
distance(F(F~),F~) ~ max(distance(F(F~),F"+I(S»,distance(F"+I(S),F~»
~ max(distance(F~,F"(S»,distance(F"+l(S),F~»
~ distance(F~,F"(S»
for every n
and hence distance(F(F~),F~) = O. We write Jl(S)F(S) for this unique fixpoint.
In order to give a semantics to object types, we first define:
14. A SEMANTICS 189
Proposition 14.2-1
If «mi:S-t Ti iEI» = ((mj:S-tRj JE/» for some cuper S, then I = 1and Ti = Ri for all i E I.
Proof
If j t I but j E I, then there exists x such that x(mj) = *, (x,x) E ((mi:S-tTi iEI)) but (x,x)
t ((mj:S-tRjM)); for example, let x be ((mj=*, mk=.1 k~j». If I = Ibut (x,y) t Ti and (x,y)
E Ri, then (A(Z)X,A(Z)y) t S-tTi (since S must be nonempty) while (A(Z)X,A(Z)Y) E
S-tRi, hence ((mi=A(z)X, mk=.1 1o<i», ((mi=A(z)y, mk=.1 k~i») t ((mi:S-tTi iEI» while
(((mi=A(z)X, mk=.1 4i», ((mi=A(z)y, mr.1 k~i») E ((mj:S-tRji<J».
o
We say that A(S)((mi:S-t Ti iEI» extends A(S)((mj:S-tTj i<J» if I ~ I, and write:
A(S)((mi:S-tTi iEl» ~ A(S)((mj:S-t Tj M))
Using this relation on Gen, we define a new construction on cupers:
[(mi:Ti iEI)] ! U {J.1(S)F(S) I F E Gen, F ~ A(S)((mi:S-tTi iEI»)
The type ((mi:Ti iEI)) can be viewed as an object type, with methods mi and result types
T i . The definition is proper because if F E Gen then F(S) is contractive in S, and hence
J.1(S)F(S) exists and is unique. We explain this definition at some length in the next sec-
tion; later we show that ((mi:Ti iEI)) is contractive in each Ti .
Proof
If (X,X') E J.I.(S)F(S) then (Pn(x),Pn(x '» E J.I.(S)F(S) by uniformity, and hence (Pn(x),
Pn(x'» E F"'(S) for every S and for almost all m since distance(J.I.(S)F(S),F"'(S» 5 1 /
21n+l. Moreover, we obtain that F"'(S) ~ Gm(S) by induction on m. Hence (Pn(x),
(Here we write Int and Color for their respective interpretations, informally.) Since F ~
A(S)«x,y:S-+lnt, c:S-+Color» implies F ~ A(S){(x,y: S~Int)), we obtain that:
U {J.I.(S)F(S) I F E Gen, F~ A(S)«x,y: S~Int, c:S-+Color»}
I: U {J.I.(S)F(S) I F E Gen, F ~ A(S)«x,y:S-+lnt»}
is not necessarily the case that if R is included in ((mi:Ti i.1n then R can be expressed in
the form ((m/Tjj·,)). For example, if (0,0) E ((m;:Ti id)1 then R could be C({(o,o))). In light
of this example, we are led to restrict R to range over cupers of the form U {~(S)F(S) I F
E Gen, F~ G} for some G ~ t..(S)«mi:S--,T; i.1)). This argument suggests that we may
account for structural subtyping by focusing attention on cupers of the form u {~(S)F(S)
IF E Gen, F ~ G}, and by saying that u {~(S)F(S) I F E Gen, F:::; G} is a "structural sub-
type" of U {~(S)F(S) I F E Gen, F ~ H} only if G :::; H. Although this definition seems rea-
sonable, we do not use it because it requires a special treatment of object types and
some amount of fairly syntactic argumentation.
Proposition 14.2-5
If (x,y) is finite and (x,y) E U(P), then (x,y) E P or (x,y) = (.1,.1).
Proof
Assume (x,y) E U(P) and (x,y)"* (.1,.1). Let x = Uk Xk and y = Uk YJrJ where (X"'Yk) is
an increasing sequence in P. Since (x,y) is finite, there exists k such that (x,y) =
(X"'Yk).
D
Proposition 14.2-6
If P is a uniform binary relation, then so is U(P).
Proof
Consider (x,y) E U(P). We wish to show that (Pi(X),Pi(Y)) E U(P) for all i. In case (x,y)
= (.1,.1), this is trivial, as (Pi(X),Pi(Y)) = (.1,.1). Otherwise, let x = Uk Xk and Y = Uk Y'"
where (X"'Yk) is an increasing sequence in P. Clearly (plX),Pi(Y))!; (Uk X",Uk Yk), and
since (Pi(X),Pi(Y)) is finite there exists j such that (Pi(X),Pi(Y))!; (Xj,Yj). By monotonic-
ity and idempotence of pi, (Pi(X),Pi(Y)) !; (Pi(Xj),Pi(Yj))' On the other hand, (Xj,Yj) !;
(x,y), and hence (Pi(Xj),Pi(Yj))!; (Pi(X),Pi(Y)). Hence for all i there exists j such that Pi(X)
= Pi(Xj) and Pi(Y) = Pi(Yj) . Since P is uniform and (Xj,Yj) E P, it follows that (Pi(X),Pi(Y))
E P for all i, and hence that (Pi(X),Pi(Y)) E U(P) for all i.
D
Proposition 14.2-7
If P is a uniform binary relation, then U(P) is complete.
Proof
Clearly, (.1,.1) E U(P). In addition, consider an increasing sequence in U(P), (Xi,Yi),
with limit (x,y). We show that (x,y) E U(P) by finding an increasing sequence
(x/,y/) in P with limit (x,y).
The case of P empty is trivial. In case P is nonempty, let (x/,y/) = (Pi(Xi),Pi(Yi)) . If
(Xi,Yi) is increasing, then so is (plXi),Pi(Yi))). Since completion preserves unifor-
mity by Proposition 14.2-6, (x/,y/) E U(P). Moreover, by Proposition 14.2-5, (x/,y/)
E P (since (.1,.1) E Pas Pis nonempty).
Finally, we check that the limit of this constructed sequence is (x,y). Clearly,
(Ui X/,Ui Y/)!; (x,y), since (x/,y/)!; (Xi,Yi) for all i and (x,y) =(Ui Xi,Ui Yi). To show that
(x,y) !; (Ui x/,u; y/), we prove that for all j there exists i such that (Pj(x),Pj(Y)) !;
(x/,y/). By finiteness considerations, for all j there exists k such that (Pj(x),Pj(Y)) !;
(X"'Yk). Letting i = max(j,k), we obtain (Pj(x),Pj(Y)) !; (X;,Yi), and then (Pj(x),Pj(Y)) !;
(p;(Xi),Pi(Y;)) = (x;',y;' ), the desired result.
D
14. A SEMANTICS 193
Proposition 14.2-8
If P E NUPER, then U(P) E CUPER.
Proof
We check that U(P) E NUPER, and that in addition U(P) is complete.
• Completion preserves nonemptiness.
• Completion preserves symmetry.
• Completion preserves uniformity by Proposition 14.2-6.
• Completion preserves transitivity in the presence of uniformity. If (x,y), (y,z) E
U(P), then (Pi(X),Pi(y)), (Pi(y),Pi(Z)) E U(P) for all i, by uniformity, and (Pi(X),Pi(y)),
(Pi(y),Pi(Z)) E P for all i by Proposition 14.2-5. By transitivity of P, (Pi(X),Pi(Z)) E P for
all i, and hence (x,z) E U(P).
• Completion establishes completeness in the presence of uniformity by Proposition
14.2-7.
D
Proposition 14.2-9
Let P E NUSR. Then C(P) = U(T(PO)).
Proof
Let Q = po; note that Q E NUSR. First, P ~ U(T(Q)), and in fact P ~ U(Q): if (x,y) E
P, then (Pi(X),Pi(y)) E P for all i by uniformity, and hence (Pi(X),Pi(y)) E Q for all i;
therefore (x,y) E U(Q). Next, U(T(Q)) E CUPER by Propositions 14.2-4 and 14.2-8.
Finally, consider any other cuper R such that P ~ R. We show that U(T(Q)) ~ R.
Since U and T are monotonic and U(T(R)) = R, we have that U(T(P)) ~ R. So it suf-
fices to remark that U(T(Q)) ~ U(T(P)).
D
Proposition 14.2-10
Let P, Q E NUSR. Assume that f and g are continuous functions such that (j{x),fty))
E Q and (j{x),g(y)) E Q for all (x,y) E P. Then (j{x),g(y)) E C(Q) for all (x,y) E C(P).
Proof
Assume that (x,y) is finite. First we argue that if (x,y) E C(P), then for some zo, . . . ,
Zn, we have x = Zo P Zl . . . Zn-1 P Zn = y. By Proposition 14.2-9, C(P) = U(T(PO)); so if
(x,y) E C(P), then (x,y) E T(PO) by Proposition 14.2-5 (since clearly (.1,.1) E PO). So
there exist zo, .. . , Zn such that x = Zo po Zl . .. Zn-1 po Zn = y, and hence x = Zo P Zl . . .
Zn-1 P Zn = y. By hypothesis,j{x) =j{zo) Qj{Zl) ... j{Zn-1) Qg(zn) = g(y). Since Q ~ C(Q)
and C(Q) is transitive, it follows that (j{x),g(y)) E C(Q).
Now consider (x,y) not necessarily finite. If (x,y) E C(P), then for all i, (Pi(X),Pi(y)) E
C(P), by uniformity. Now the argument for finite elements yields (j{Pi(X)),g(Pi(y)))
194 PART II. SECOND-ORDER CALCULI
Proposition 14.2-11
If Ri E CUPER for all i E I and S E CUPER, then (UiEl Ri)-7S = ()iEI (R i-7S).
Proof
Since (UiEI Ri)-7S S;;; Ri-7S for each i E I, (UiEI Ri )-7S S;;; ()iEI (R i-7S). In order to prove
the converse, consider two functions f g such that (fg) E ()iEI (Ri-7S). The assump-
tion that (fg) E ()iEI (R i-7S) implies that if (x,y) E ViEI Ri then (jtx), fly)) E Sand
(jtx),g(y)) E S. Since ViEI Ri E NUSR, by applying Proposition 14.2-10 we obtain
that (x,y) E C(ViEI Ri ) implies (jtx),g(y)) E C(S); that is, (x,y) E UiEI Ri implies (fix),
g(y)) E S. Therefore (f,g) E (UiEI Ri)-7S.
o
As a special case of this proposition, we obtain:
Proposition 14.2-12
For all n, if Ri E CUPER for all i E I, Sj E CUPER for j E /, and for all i E I there exists
j E / such that Pn(Ri) ~ Pn(Sj), then Pn(UiEI Rj ) S;;;; pn(UjE' Sj).
Proof
If for all i E I there exists j E / such that Pn(R i ) S;;; Pn(Sj), then (Pn,Pn) E ()iEI (R i-7(ujEJ
Sj)). By Proposition 14.2-11, (Pn-Pn) E (UiEI Ri)-7(UjE' Sj), hence Pn(UiEI Ri) S;;;; (UjE' Sj),
and immediately Pn(UiEI Ri) ~ Pn(UjEI Sj).
o
Proof
We give the proof for k = 1; the general case is similar to this one. Let Rand R' be
equal up to rank n (at least). Since Gi is nonexpansive for all i EO I, we obtain that
Gi(R) and Gi(R') are equal up to rank n. We show that «mi:Gi(R) iEI» and «mi:Gi(R')
iEI» are equal up to rank n+ 1. Reasoning by contradiction, suppose that (0,0') has
rank n+ 1 and that (0,0') EO «mi:Gi(R) iEI» but (0,0') ;. «mi:Gi(W) iEI». Since (0,0') EO
«mi:Gi(R) iEI», either (0,0') = (.1,.1) or (0,0 ' ) EO (L--7D) x (L--7D). Since (0,0') ;. «mi:Gi(R')
iEI», it must be that (0,0') EO (L--7D) x (L--7D), and (o(mi),o'(mi» ;. Gi(R') for some i EO
I; but (o(mi),o'(mi» EO Gi(R) because (0,0') EO «mi:Gi(R) iEI». Because (0,0') has rank n+ 1,
(o(mi),o'(mi» is either (Pn(o(mi»,Pn(O'(mi))) or (.1,.1), depending on whether n S i. In
either case, this means that (o(mi),o '(mi» has rank at most n. In short, we have found
an index i EO I and a pair (o(mi),o'(mi» of rank at most n such that (o(mi),o'(mi» EO Gi(R)
and (o(mi),o'(mi»;' Gi(R'); this contradicts the fact that Gi(R) and Gi(R') are equal up
to rank n.
D
Proposition 14.2-14
If Gi(R1, ...,Rk) is nonexpansive in R1, ... , Rk for all i EO I, then [[mi:Gi(R1, . .. ,Rk) iEI)) is
contractive in R1, ... ,Rk.
Proof
We give the proof for k = 1. Let Rand R' be equal up to rank n. We show that for
every extension F of A(S)«mi:S--7Gi(R) iEI» there exists an extension F' of
A(S)«mi:S--7Gi(R') iEI» equal to F up to rank n+ 1. Consider F EO Gen such that F ~
A(S)«mi:S--7Gi(R) iEI}}. If F = A.(S)«mi: S--7G i(R) iEI, mj:S--7Tj M», let F' = A(S)«mi: S--7
Gi(R') iEI, mj:S--7Tj jE/». Now, F' EO Gen and F' ~ A(S)«mi:S--7Gi(R') iEI». Furthermore,
we obtain that F and F' are equal up to rank n+ 1, because each Gi is nonexpansive,
»
--7 is contractive, and « ... is contractive by Proposition 14.2-13. Then J.I.(S)F(S) and
J.I.(S)F'(S) coincide up to rank n+ 1, and hence [[mi:Gi(R) iEI)) is included in [[mi:Gi(R')
iEI)) up to rank n+ 1 by Proposition 14.2-12. We obtain the converse analogously.
Hence [[mi:Gi(R) iEI)) and [[mi:Gi(R') iE1)) coincide up to rank n+ 1.
D
Proposition 14.2-15
If G(R1, ... ,Rk+l) is contractive (nonexpansive) in R1, ... , Rk+l and H(R1, ... ,Rk) is non-
expansive in R1, . . , Rio then URk+1ECUPER, Rk+H;;H(Rl, ...,Rk) G(R1,· ..,Rk+l) is contractive
(nonexpansive) in R1, .. . ,Rk.
Proof
Again we give the proof for k = 1, and we prove only the contractiveness claim as
the other one is similar. Suppose that Rand R' are equal up to rank n. We want to
prove that UR2ECUPER, R201(R) G(R,R2) and UR2ECUPER, R2<;;H(R' ) G(R',R2) are equal up to
196 PART II. SECOND-ORDER CALCULI
rank n+ 1. Let (x,x ') E UR2ECUPER, R2\;H(R ) G(R,R2) be a pair of rank n+ 1. We wish to
show that (x,x') E UR2ECUPER, R2\;H(R') G(R',R2)' By Proposition 14.2-12 it suffices to
prove that if (x,x') E G(R,R 2 ) for some R2 ~ H(R) then (x,x ' ) E U R2ECUPER, R2\;H(R')
G(R',R2), by proving that (x,x') E G(R',R2') for some R2' E CUPER such that R2' ~
H(R '). So assume that (x,x' ) E G(R,R2) and R2 ~ H(R). Since G is contractive in R2
and (x,x') is of rank n+ 1, we have (x,x') E G(R,Pn(R 2» . Similarly, since G is contrac-
tive in R, and Rand R' are equal up to rank n, we have (x,x ') E G(R',pn(R2». In addi-
tion, Pn(R 2) E CUPER and Pn(R 2) ~ H(R). Since H is nonexpansive in R, and Rand
R' are equal up to rank n, we have also Pn(R2) ~ H(R'). It follows that (x,x ' ) E
UR2ECUPER, R2\;H(R ' ) G(R',R2)'
o
Proposition 14.2-16
If G(R), .. .,Rk+) is contractive (nonexpansive) in R), .. ., Rk+) and H(R), ... ,Rk) is non-
expansive in R), .. ., RJv then nRk+IECUPER, Rk+I~H(RI, . . .,Rk) G(R), .. .,Rk+) is contractive
(nonexpansive) in R), ... ,Rk•
Proof
We give the contractiveness proof for k = 1. Suppose that Rand R' are equal up to
rank n. We want to prove that nR2ECUPER, R2\;H(R) G(R,R2) and nR2ECUPER,
R2~H(R') G(R ',R2) are equal up to rank n+ 1. Let (x,x') E nR2ECUPER, R2\;H(R) G(R,R2) be a
pair of rank n+ 1. Pick R2 such that R2 E CUPER and R2 ~ H(R ' ), to show that (x,x')
E G(R',R2). It will follow that (x,x') E nR2ECUPER, R2\;H(R ' ) G(R',R2). Since Rand R'
agree up to rank nand His nonexpansive, R2 ~ H(R ' ) implies Pn(R 2) ~ H(R). In
addition, Pn(R2) E CUPER. Hence (x,x') E G(R,Pn(R 2» . Since G is contractive in R, it
follows that (x,x ' ) E G(R ',pn(R2». Since G is contractive in Rz, it follows that (x,x') E
G(R',R2).
o
where TV is the set of type variables and TE the set of type expressions. A mapping TJ
in TV--7CUPER is a type environment. We write [A~ for the semantics of a type A with
the environment TJ. We set:
[X~ g, TJ(X)
[Top~ g, Univ
[[mj:Cj jel..nl~ g, ((mj: [C~ jel..nn
[A--7BD'l g, [A~--7[B~
[\t(X< :B)A~ g, nR ECUPER, R!::IBhJ [A~(X<-R)
[3(X<:B)A~ g, UReCUPER, RQ BhJ [A~ (X<-R)
[J.I.(X)A~ g, J.I.(T)(Univ--7[AD'l(x<-n)
The definition for recursive types is the most unusual one. A simpler alternative
would have been: [J.I.(X)A~ g, J.I.(T)[A~ (x<-n . The simpler definition would have yielded
the attractive property [ J.I.(X)A{X}~ = [A«J.I.(X)A» D'l' but we do not need that property.
Moreover, the simpler definition requires special treatment for some types, such as
J.I.(X)X; technically, it works well only for types that are formally contractive [17, 82]. The
definition that we have given does not depend on formal contractiveness; it yields
[J.I.(X)A{X}~ = [ J.I.(X)A«J.I.(X)A » ~ but not [J.I.(X)A{X}~ = [A(J.I.(X)A»~ .
The following proposition implies that the definition is proper in all cases:
Proposition 14.3-1
If A is a well-formed type expression, then [A~ (X<-R) is nonexpansive in R, and
hence Univ--7[A~ (X<-R) is contractive in R.
Proof
The proof is by induction on the structure of A. The cases of type variables and --7
are well known. It is also well known that metric fixpoints preserve nonexpansive-
ness, and hence if [A~( y<- n is non expansive in R then so are both Univ--7 [A~(y<-n
and J.I.(T)(Univ--7[A~(y<-n). In fact, Univ--7[A~(y<-n is contractive in T, so when X ==
Y we have that J.I.(T)(Univ--7[A~(x<-n) is well defined. The case of Top is trivial.
Propositions 14.2-15 and 14.2-16 deal with bounded quantification. Proposition
14.2-14 deals with object types.
o
It follows that every well-formed type expression denotes a non expansive function on
CUPER (as a function of its free variables).
where V is the set of variables and E the set of expressions. A mapping p in V --7D is an
environment. We write [aD p for the semantics of a term a with an environment p. The
198 PART II. SECOND-ORDER CALCULI
semantics of terms does not depend on the semantics of types; all type information is
erased in the course of the interpretation of terms. Therefore we do not need a type
environment.
[X]p ! p(X)
[[mi=<;(xi:A)ci if1n p ! ((mi=[A(Xi:A)ci]p iff»
[a.m]p ! i{[a]p E (L~D) and [alp(m) E (D~D)
then [a]p(m )([a]p) else *
[a . m~<;(x:A)c]p ! i{[a]p E (L~D)
then [a]p(m~[A(x:A)c]p) else *
[A(x:A)b]p ! A( v )[ b]P(X+-V)
[b(a)]p ! i{[b]p E (D~D) then [b]p([a]p) else *
[A(X<:A)bllp ! [b]p
[b(A)]P ! [b]p
[pack X<:A=C with b{X) :B{X)]p ! [b]p
[open cas X<:A,x:B in d{x):D]p ! [d)P(XHCIP)
[fold(A,a)Dp ! A(v)[a]p
[un!old(a)]p ! [a]p(.L)
This definition is given in a metalanguage where v E V is a strict membership test,
and where conditionals and conjunctions are strict and evaluated left to right, for
example [a.m~<;(x:A)c]p = .L if [alp = .L . Thus the interpretation of each term is a contin-
uous function of the interpretation of its free variables.
Note how the semantics turns <;'s into A'S and objects into records. In a sense, then,
it is easy to encode an object calculus in a record calculus (essentially using the ideas of
the self-application semantics). The catch is that the denotations of object types are not
standard record types.
Note also that, in the semantics, updating a method of an object does not produce
an error if the object does not have the method in the first place. This definition of
update simplifies our technical treatment.
14.3.3 Soundness
We say that E and 11 are consistent in the usual sense: if X<:A appears in E, then l1(X) ~
[A~. We say that E, 11, and (p,p') are consistent when, in addition, if x :A appears in E,
then (p(x),p'(x» E [A~ . Next we check that, if 11 and (p,p') are consistent with E, then E
f- B <: A implies [B])" ~ [Ah E f- a : A implies ([a]p,[a)p' ) E [A])", and E f- a H a' : A implies
([a]p,[a']p') E [A])". An immediate corollary of these verifications is that no well-typed
term has * as its denotation.
The proof of soundness is by induction on derivations. We start with the subtyping
rules.
Lemma 14.3-2 (Soundness of sub typing rules)
The sub typing rules are sound.
14. A SEMANTICS 199
Proof
We treat only two interesting rules. The arguments for other rules are standard.
This follows immediately from the definition of the semantics and from Proposi-
tion 14.2-3.
(Sub Rec)
E f- Il(X)A E f- Il(Y)B E, Y<:Top, X<:Y f- A <: B
E f- Il(X)A < : Il(Y)B
Consider TJ and (p,p') consistent with E, and R, T E CUPER such that R ~ T. Let ~
= TJ(Yf-T)(Xf-R). Since ~ and (p,p') are consistent with E, Y<:Top, X<:Y the third
hypothesis implies that [A~ ~ [B~. According to the other hypotheses, XtFV(B)
and Y~FV(A); so [AJh,(X ....R) ~ [BJh,(Y.... n . Hence Univ--7[AJh,(X....R) ~ Univ--7[BJh,(y....n .
Let F be the function that maps a cuper R to Univ--7[AJh,(X<-R) and G be the function
that maps a cuper T to Univ--7[BJh,(y.... n. We have just obtained that R ~ T implies
F(R) ~ G(T). Since these functions are contractive, Proposition 14.2-2 yields that
Il(S)F(S) ~ Il(S)G(S), that is, that [1l(X)AJh, ~ [1l(Y)BJh,.
o
The proof of the soundness of the equational rules is given in Appendix C.4, as
Lemma C.4-7. The arguments for the value typing rules are special cases of those for
the equational rules.
We obtain the following soundness results:
Theorem 14.3-3
Assume that TJ and (p,p') are consistent with E. Then, for derivations in FOb<:Il:
If E f- A, then [AJh, E CUPER.
If E f- A <: B, then [AJh, ~ [BJh,.
If E f- a : A, then ([aDp,[aDp') E [AJh,.
If E f- a H a' : A, then ([aDp,[a'Dp') in [AJh,.
o
By reversing the last two implications, it follows that:
• If [aDp = *, then E If a : A, so the type theory is sound.
• If [aDp ~ [a'Dp" then E If a H a' : A, so the equational theory is consistent.
15 DEFINABLE COVARIANT SELF TYPES
In this chapter, we resume the study of Self types. We work entirely within the calculus
Ob q 1 of Chapter 13 (namely, the second-order calculus with bounded quantifiers,
recursion, and simple object types). Within this calculus, we derive rules for the com-
bination of object types and the Self quantifier.
We show how, using those derived rules, we can easily provide typings for the
examples of Section 6.5. In particular, we can give satisfactory types to objects with
methods that return self. However, we find that updating methods that return self is
difficult, and so is the definition of sufficiently parametric pre-methods for use in
classes. We explore two solutions to these problems. The first solution simply relies on
adding a trivial auxiliary method to objects. The second solution consists in assuming
stronger invariants about objects, and in building those invariants into our rules. The
second solution leads us to a reformulation of the rules for Self types that we develop
in Chapter 16.
Self types, as we define them in this chapter, are not intended for use in the types
of binary methods. We discuss binary methods briefly, and postpone a fuller discus-
sion to Part III.
15.1 <;-Objects
The payoff of the Self quantifier, and particularly of the (Sub Self) rule derived in Sec-
tion 13.5, comes when it is used in conjunction with object types. Object types with Self
are obtained by the combination of the simple object types of Chapters 7 and 8 with the
Self quantifier of Section 13.5. These new types allow subsumption between objects
containing methods that return self.
Note that we do not require that [li:BdX} i€1..n] be covariant in X, only that each
BdX} be covariant in X. (In fact, [li:BdX) i€1..n] is invariant in X as soon as X occurs in
some Bi{X} .) Although the Self quantifier of Chapter 13 has no covariance restrictions,
we shall see that the covariance requirement is necessary when selecting components
of <;-objects.
The covariance requirement implies that Xi must not occur within any object type
within Bi, since object types are invariant in their components. For example, the type
<;(X)[I:<;(Y)[m:X, n:Yll violates the covariance requirement. Hence, informally, we may
say that Self types do not nest: there is a single meaningful Self type within each pair
of object brackets.
It is often useful to consider an unfolding A(C) == [li:Bi(CJ i€1..n] of a <;-object type A
== <;(X)[li:BdX+) i€1..n], for C <: A . We frequently consider A(X), for a variable X, and the
self-unfolding A(A) of A. (When building an element of type A it is common to build first
an element of type A(A).) Together, a type C <: A and an element of A(C) can be used
for building an element of A; we say that C is the representation type for this element.
(Eq Sub C;0bject) (where A;: C;{X)[l;:B;{X+/ ;<1..11), A';: qX)[I;:B;{X+/ ;EI..II+m))
In the first two rules, (Type ~Object) and (Sub ~Object), the antecedents require
that each Bi{XI be covariant in X. Whenever a type of the form ~(X)[li:Bi{X+ 1 iEl..nj
appears in the antecedents of the other rules, the covariance condition is thus ensured .
The most remarkable fact about ~-object types is that the rule (Sub ~Object) holds.
In contrast, an example in Section 9.5 shows that a rule analogous to (Sub ~Object) but
where ).l replaces ~ is unsound.
The rule (Val ~Object) can be used for building a ~-object wrap(Y<:A=C)b{Y) from
a subtype C of the desired ~-object type A, and from a regular object bK&. The Yvari-
able in b{YI is the Self type, in case the methods of b need to refer to it. When construct-
ing a fixed ~-object, its methods are not required to operate on an arbitrary self: they
just need to match the given representation type of the object being constructed. That
is, to construct a ~-object of type A == ~(X)[li:Bi{X+1 i.l ..nj it suffices to have a set of meth-
ods bj : BjlC& with C <: A (not bj : Bj{XI for an arbitrary X <: A). Moreover, each of these
methods can assume the existence of a self parameter Xj : [li:BiiC» iEl..nj.
The ~-object selection operation aA.lj reduces fairly simply to a regular selection
operation on the underlying object.
The ~-object update operation a.lj~(Y< : A,y: A(Y)C;(x:A(Y)b is more interesting,
although similarly it reduces to an update operation on the underlying object. The
method body b must produce a result of type Bj8Yl parametrically in Y.
The rule (Eq ~Object) yields a flexible congruence lemma, analogous to the one
obtained from the (Eq Pack<:) rule for existentials:
(Eq ~Object Lemma) (where A == ~(X)[li : Bi{X+) iEl..n), A';: 9 X )[li:Bi{X+} iEl..n+m))
E f- C <: A' E f- biC& H b'(C& : A(C) E f- b'ftC» : A'(C)
E f- wrap(Y<:A=C)b{YI H wrap(Y<:A'=C)b'{YI : A
The rule (Eq Sub ~Object) is obtained from (Eq Sub Object) and (Eq Wrap Lemma).
This rule is of limited power because the same type C appears on both sides of the equa-
tion in the conclusion. We can trace back this limitation to a similar limitation in the
rules for existential types.
15. DEFINABLE COVARIANT SELF TYPES 205
We have the inclusion P 2 <: PI, by (Sub C;0bject). (Compare with Section 9.5.)
Next we define the one-dimensional origin point, where Self names the type of the
object, PI' Recall that wrap(A,c) abbreviates wrap(X<:A=A)c for an unused X, and that
wrap(X=A)c abbreviates wrap(X<:A=A)c. A common pattern in the code below is that
methods for a C;-object type A use the construct wrap(X,c) to produce a value of type X
from a value c of type A(X).
origin I :PI ~
wrap(Sel!=P1)[x = 0, mv_x = <;(s:P1(Self)) A(dx:lnt) wrap(Self, s.x := s.x+dx)]
The typing originl : PI can be derived as follows, with PI(P I ) == [x:lnt, mv_x:lnt~PI] as
[
the chosen representation type for PI'
Then we have both BkPj <: P j and BkPj <: Bk. Using the same techniques as in the pre-
vious example we obtain the following typing:
0 : BkPj g,
wrap(Self=BkPj)
[retrieve = C;(s:BkPj(Self» wrap(Self, s),
backup = C;(s:BkPj(Self»
wrap(Self, s.retrieve ~ C;(s':BkPj(Self» wrap(Self, s»,
X= •.. ,
mv_x= ... )
15.2.3 Booleans
In preparation for the next example, we show an encoding that uses quantified types.
As we saw in Section 7.4.2, the booleans can be typed as follows:
BoolA g, [if A, then:A, else:A)
The definition depends on a result type A for conditional expressions.
We can achieve a more uniform typing by using quantifiers, so that a single poly-
morphic type can account for all uses of booleans:
Bool g, Ii(X)[if:X, then:X, else:X)
true g,
A(X)[if = C;(x:Boolx) x.then, then = C;(x:Bool x ) x t.hen, else = C;(x:Bool x ) x.else)
false g,
A(X)[if = C;(x:Boolx) x.else, then = C;(x:Boolx) x.then, else = C;(x:Bool x ) x e. lse)
ifA b then c else d g,
((b(A).then ~ C;(x:BooIA)c).else ~ C;(x:BoolA)d).if x~FV(c)uFV(d)
15.2.5 A Calculator
This is the typed version of the example in Section 6.5.4. Once the code is properly
annotated, the calculator has type:
C £ C;(Self)[arg,acc:Real, enter:Real~Self, add,sub:Self, equals:RealJ
The code is:
calculator: C £
wrap(Self=C)
[arg = 0.0,
acc = 0.0,
enter = c,(s:C(Self) A(n:Real) wrap(Self, s.arg := n),
add = c,(s:C(Self)
wrap(Self, (s .acc := s.equals).equals ~ c,(s':C(Self) s'.acc+s'.arg),
sub = c,(s:C(Se/f)
wrap(Self, (s.acc := s.equals).equals ~ c,(s':C(Self» s'.acc-s'.arg),
equals = c,(s:C(Self» s.argJ
By subsumption, the calculator also has type:
Calc £ C;(Self)[enter:Real~Self, add,sub:Self, equals:RealJ
This shorter Calc type is the one shown to users of the calculator.
A scientific calculator could also be defined with additional state and operations.
Its inner design might be quite different from that of our basic calculator, but the scien-
tific calculator's type could still be a subtype of Calc.
208 PART II. SECOND-ORDER CALCULI
originleq £
wrap(Self=Pleq)[x = 0, mv_x = ... , eq = c;(s:Pleq(Self)) A(p:Self) s.x =1.1 poX)
Binary methods violate the covariance requirement of C;-object types. We temporarily
ignore this requirement, which is just a convention. We use the rules for the C; quanti-
fier, which do not depend on covariance, instead of the derived rules for C;-objects.
We discover that binary methods cannot be invoked effectively because of typing
restrictions. Expanding the encoding of method invocation from Section 15.1, we may
try to pull out the eq method from a C;-object p of type Pleq:
use p as Self<:P leq, z:[x:Int, mv_x:Int-7Self, eq:Self-7Bool)
in z.eq : Pleq -7Bool (not typable)
The rule (Val Use) requires that z.eq be given a type independent of Self. This is not pos-
sible because Self is in contravariant position in Self-7Bool. (In contrast, we can pull out
z.mv_x: Int-7P leq because In t-7 Self <: Int-7P leq .)
Although we cannot pull out the eq method from p, we can still apply it within the
scope of the use construct if we can find an adequate argument. One possibility is:
use p as Self<:Pleq, z:[x:lnt, mv_x:Int-7Se/f, eq:Self-7Bool)
in z.eq(z.mv_x(I)) : Bool
which compares p with its translation by 1, returning false. However, most advanta-
geous uses of eq would involve comparisons with independently obtained points, and
these comparisons are not possible. This situation arises, essentially, because indepen-
dent instances of the same existential type do not intermix.
To avoid the contravariant occurrence of Self, we may define an alternative to Pleq:
PI £ C;(Self)[x:Int, mv_x:Int-7Self)
Pleq , £ C;(Self)[x:Int, mv_x:lnt-7Self, eq:PI -7Bool)
originl eq ' ~
wrap(Se/f=Pleq')[x = 0, mv_x = ... , eq = C;(s:Pleq.[Self)) A(P:P I ) s.x =1.1 pox)
These definitions are more useful than the previous ones. In particular, originleq,.eq(p)
is well-typed whenever p has type PI. However, eq is no longer a binary method
because its argument does not have the same type as self.
Binary methods are essentially incompatible with subsumption. In contrast, the
purpose of covariant Self types is to work well with subsumption. We may conclude
that Self types as we have defined them are not well suited for modeling binary meth-
ods. We discuss the handling of binary methods again in Part III.
If the type of the updated method is the Self type, it is critical that the new method
return a result of the "true type" of c, because one of the other methods may be invoked
on the result. Therefore the new method must return self or a modified self. As in Sec-
tion 9.6, we say that the new method must be parametric in Self.
This is where we need the complex rule for updating ~-objects, (Val ~Update),
which was not used in Section 15.2 even when typing update operations. Consider, for
example, the type:
A ~ ~(Self)ln,flnt, m:Self] with A(X) == In,fInt, m:X]
An updating method for a == wrap(Y<:A=C)b/YI can use in its body the variables
Self<:A, x':A(Self), and x:A(Self), where x' is in fact b«q, according to (Eval ~Update),
and x is the self of the new method. We can therefore update the f method of A with
methods that produce integers, in any of the following ways.
a./ ~ (Self<:A, x':A(Self)) ~(x: A(Self)) 3
setting a.fto produce 3 (constantly)
a.f ~ (Self<:A, x':A(Self)) ~(x:A(Self)) x'.n + 1
setting a.fto produce b.n + 1 (constantly)
a.f ~ (Self<:A, x':A(Self)) ~(x:A(Self)) x.n + 1
setting a.fto produce 1 + the value of n when/is invoked
Let us now attempt to update the m method. The typing rule (Val ~Update)
requires that, from the variables Self<:A, x':A(Self), x:A(Self) at its disposal, the updat-
ing method must produce a value of type Self. Here are some possibilities:
a.m ~ (Sel/<:A, x':A(Self)) C;(x:A(Self) x'.m
setting a.m to produce the current b.m (constantly)
a.m ~ (Self<:A, x':A(Self)) C;(x:A(Self)) x.m
setting a.m to diverge
However, we cannot update a.m with anything useful. Note, first, that we cannot
synthesize a value of type Self from scratch. Second, we cannot return x' or x, nor
wrap(A,x,) or wrap(A,x), because none of these can be given type Self. Third, any update
to x', x, wrap(A,x'), or wrap(A,x) preserves their original type, so we cannot return the
updated terms either. Finally, wrap(Self,x): Sel/is not derivable, for an unknown Self <:
A; see (Val ~Object) .
Moreover, it would be unsound to ignore these typing problems and return, say,
wrap(Self,x) or wrap(Self,x'). The reason can be seen in the following example, which
builds a ~-object r2 from a proper subtype Rl of its own type R 2 :
R2 ~ ~(Self)[p:Self, q:Int]
Rl <: R2 ~ ~(Self)[p:Self, q:Int, t:Int]
rl : Rl ~ wrap(Y<:R 1=R 1 )[p = ~(S:Rl(Y) wrap(Y, s), q = 0, t = 0]
r2 : R2 ~ wrap(Y<:R 2=R 1 )[p = '1, q = ~(s:R2(Y) s.p.t]
212 PART II. SECOND-ORDER CALCULI
15.6 Recoup
In this section we introduce a special method called recoup with an associated run-time
invariant. A recoup method is a method that returns self immediately. The invariant
asserts that the result of a recoup method is its host object. These simple definitions
have a number of surprising consequences.
fr
(E, s:[r:R) f- s : [r:R) by (Val x)
~, s : [r~ R! f- wrap(Z<:R=~)S : R (Val Wrap)
= E, s.[r.R) f- wrap(R,s) . R (standard abbreviation)
E f- [r=~(s : [r:R)wrap(R,s») : [r:R) (Val Object)
E f- wrap(SeIJ< : R=R)[r=~(s:[r: SelJ)wrap(Self,s») : R (Val Wrap)
== E f- wrap(SeIJ=R)[r=~(s:R(Se/fl)wrap(Self,s») : R (standard abbreviation)
We say that a method of the form ~(s:A(Self)wrap(Self,s), in the context of a C;-object of
the form wrap(SeIJ=A)[ ... ), is a recoup method. The presence of a recoup method in a
15. DEFINABLE COVARIANT SELF TYPES 213
C;-object implies that the representation type of the object is the same as the true type of
the object, rather than a proper subtype of the true type.
The backup example from Section 15.2.2 defined the type:
Bk ~ C;(Self)[retrieve:Self, backup:Selfl
As we have just discussed, we cannot usefully update the retrieve or backup methods of
Bk, or any method with result type Self This is because we are not able to produce a
useful element of type Self from an element y : [retrieve:Self, backup:Selfl as would be
required by the (Val C;Vpdate) rule, other than y.retrieve and y.backup. We now intro-
duce a technique that solves this problem.
We add a new component, called recoup, to the type Bk, and consider objects where
recoup is set to C;(s:Bk(Bk))wrap(Bk,s). We redefine:
Bk ~ C;(Self)[recoup:Self, retrieve:Self, backup:Selfl
a : Bk ~ wrap(Self=Bk) b
where b :;
[recoup = C;(s:Bk(Self)) wrap(Self, 5),
retrieve = C;(s:Bk(Se/f)) wrap(SeZf, 5),
backup = C;(s:Bk(SeZf)) wrap(Self, s.retrieve := wrap(Self, 5»1
Then the following method update typechecks, since s'.recoup has type Self:
a,retrieve ~ (Self<:Bk, s':Bk(Selj)) C;(s:Bk(Selj)) s'.recoup
Moreover, the behavior obtained is a useful one, corresponding to performing a
backup "by hand" instead of calling the backup method to do it. We calculate:
(wrap(Se/f=Bk) b),retrieve ~ (Self<:Bk, s':Bk(SeZf)) C;(s:Bk(Se/f)) s'.recoup
wrap(Selj=Bk) b.retrieve ~ C;(s:Bk(Selj)) b.recoup
wrap(Self=Bk) b.retrieve ~ C;(s:Bk(SeZf)) a
wrap( Sel!= Bk)
[recoup = C;(s:Bk(Self)) wrap(Self, 5), retrieve = a, backup = ... 1
Similarly, the following update clears the backup log:
a,retrieve ~ (Self<:Bk, s':Bk(Se/f)) C;(s:Bk(Selj)) s.recoup
Intuitively, a recoup method allows us to recover a "parametric self" b.recoup,
which equals a but has type Self <: Bk and not just type Bk like a. This technique is par-
ticularly useful after an update on a value of type Bk(Self), because the result of the
update only has type Bk(Se/f), but then recoup can extract an appropriate result of type
Self
Class(P I ) ~
[new:P I,
x:'<I(Se/f<:P I ) PI (Se/f)-7Int,
mv_x:'<I(Se/f<:PI) PI(Se/fHlnt-7Se/f]
PI-class: Class(P I ) ~
[new = ~(z:Class(PI» wrap(Sel!=P I)
[recoup = ~(s:PI(Se/f) wrap(Se/f, s),
x = ~(s:PI(Se/f)) z.x(Self)(s),
mv_x = ~(s:PI(Se/f)) z.mv_x(Se/f)(s)],
x = A(Se/f<:PI ) A(s:PI(Self) 0,
mv_x = A(Se/f<:PI) A(s:PI(Self) A(dx:lnt) (s.x := s.x+dx).recoup]
Note that PI-class does not include a pre-method for recoup, although it could. Instead,
the recoup method is simply built into objects by new.
This code achieves the desired effect, but is rather complex and may seem ad hoc.
Therefore, starting in the next section, we investigate alternatives to the recoup tech-
nique.
Both structural subtyping and shape invariants must be assumed to achieve the
desired effect. Collectively we refer to both invariants as structural invariants .
Structural invariants have important consequences for typing, as we already dis-
cussed at the end of Section 13.1. In particular, the structural invariants discussed in
this section support stronger type rules for objects with Self. We call structural the rules
that exploit the structural invariants. Most of the examples discussed so far can already
be typed with the standard rules, but the stronger structural rules provide more natu-
ral typings. Other examples that could be typed only using recoup can now be typed
with the structural rules, again in a more natural way. In particular, it becomes easy to
update methods that return self.
Our goal is to define a new calculus with "structural object types" and "structural
objects". The calculus supports the stronger structural rules. To justify these rules, we
provide an interpretation of structural objects into our ~-objects that establishes a par-
ticular shape invariant, namely that the interpretation of every structural object has a
certain shape. We provide an interpretation of the operations on structural objects that
preserves the invariant under reduction. We show, within our standard formal system,
that the structural rules are derivable in the presence of side conditions about the shape
of terms and types.
obj(X=C)[lj=C;(xj:X)bdX,xj} jELn] ~
wrap(X<:C=C)[lj=C;(Xj':C(X)b;HX,wrap(X,xj') HjEln]
for C == Obj(X)[lj:BdX} jELn]
15. DEFINABLE COVARIANT SELF TYPES 217
a.lj ~
a(C).lj
for C = Obj(X)[li:B;\X} ifLn), a = obj(X=C)[li=~(Xi:X)b;\X,Xi} ifLn), jE l..n
Note the special properties of C;-objects that correspond to structural objects: the exter-
nal and internal types of wrap(X<:C=C)[li=~(X/:C(X»bi«X,wrap(X,x;')j ifLn) are the same
(C), and all occurrences of self (x;,) in the body of methods are in the context wrap(C,xi'>c
Thus, intuitively, the shape invariant is that every object has the form:
wrap(X<:C=C)[li=~(X;':C(X)biiX,wrap(X,xi')j if Ln)
and the structural subtyping invariant is that every subtype of Obj(X)[li:B;\X} ifLn) is a
longer object type, of the form Obj(X)[li: B;\X} ifLn+m).
(Sub Object')
E, X f- B;\X+} 'lfiEl .. n+m
E f- Obj(X)[/i:B;\X} ifLn+m) <: Obj(X)[li:B;\X} ifL")
(Eva) Select") (where A:; Obj(X)[li:BiIX} i<l..n], A':; Obj(X)[li:BiIX } i< l..n'],
a :; obj(X=C)[li= ~(xi: X)bi{X,xi} i<l..n+",])
Ef-a : A Ef-A<:A' jEl..n
These rules differ from the previous ones by a redundant assumption E I- A <: A ' and
by the restriction of lj to the labels of A'.
The rules we have derived so far are valid, but are not fully general: the typing rule
(Val Select") for a.lj, for example, is applicable only to an a of a specific form and not to
a variable x such that E I- x : A. Similarly, that rule is applicable only to an A of a specific
form and not to a type variable X such that E I- X <: N. A stronger rule would be:
(where A' '" Obj(X)[li:Bi{X\ i<l..n'])
El-a : A EI-A<: A' jEt .. n'
E I- a.lj : BjMi
E f- C <: BjxB2 E f- a : C E f- b : C
E f- lfst(a ),snd(b») : C
The soundness of this rule depends on the property that every subtype of a prod-
uct type Bj xB 2 is itself a product type Cj xC2. This property is true operationally for par-
ticular systems, but fails in any semantic model where subtyping is interpreted as the
subset relation. Such a model would allow the set {a,b} as a subtype of BjxB2 whenever
a and b are elements of BjxB2. Then lfst(a),snd(b») is not necessarily an element of {a,b}.
Structural rules do not seem to be very useful for standard type constructions such
as products, but they are important in the treatment of object types with Self; they are
required for typing programs satisfactorily and simply. Structural rules allow methods
to act parametrically over any X <: A, where X is the Self type and A is a given object
type, perhaps producing results of type X. We demonstrate the power of structural
rules by examples, and by our representation of classes and inheritance. We prove that
structural rules are sound for our operational semantics.
We adopt structural rules only for object types. We saw an example of such a struc-
tural rule in Section 13.1.3:
(Val Structural Update) (where A .. [/i:Bi iE!.."))
E f- a : C E f- C <: A E, x:C f- b : Bj jE l..n
E f- a.lj~c;(x:C)b : C
This rule implies that, if y: Yand Y <: A where A is a given object type with an invariant
component I:B, then we may update 1 in y with a term b : B yielding an updated object
of type Y, and not just A. This rule is based on the assumption that any Y <: A is closed
under updating of 1 with elements of B. The closure property holds in a model only if
any subtype of A allows the result of 1to be any element of B. Intuitively, this condition
may fail because a subtype of A may be a subset with I:B' for B' strictly included in B.
Operationally, the closure property holds because any possible instance of Y in the
course of a computation is a closed object type that, being a subtype of A, has a compo-
nent 1of type exactly B. The rule for update that we adopt in this chapter is analogous
to (Val Structural Update), but deals in addition with Self.
Obj(X)[livi:B;/X+} iEl .. nl
where B{X+} indicates that X occurs only covariantly in B
The binder Obj binds a Self type named X that can occur covariantIy in the result
types Bi . The covariance requirement is necessary for the soundness of our rules. As in
Section 8.7, each Vi (a variance annotation) is one of the symbols -, 0, and +, for contra-
variance, invariance, and covariance, respectively. Invariant components are the famil-
iar ones. Covariant components allow covariant sub typing, but prevent updating.
Symmetrically, contravariant components allow contravariant subtyping, but prevent
invocation. Invariant components can be regarded, by subtyping, as either covariant or
contravariant.
Syntax of S types
A,B::= types
X type variable
Top the biggest type
Obj(X)[l;tJi:Bi ie1..n] object type (Ii distinct, ViE r, 0, +})
With this definition of covariant occurrences, more than one Self may be active in
a given context, as long as the covariance requirement is respected, as in the type
Obj(X)[lD:Obj(Y)[m+:X, nO: Yll.
224 PART II. SECOND-ORDER CALCULI
An object has the form obj(X=A)[li=~(Xi:X)bi i<I .."], where A is the chosen implemen-
tation of the Self type. Variance information for this object is given as part of the type
A. All the variables Xi have type X (so the syntax is somewhat redundant).
Method update is written a.I;:(Y<:A,y:Y)~(x:Y)b, where Y denotes the unknown
Self type, y denotes the old self (the object a), and x denotes self (the self at the time the
updating method is invoked). We saw the parameter y arise from the encoding of Self
types in Chapter 15; it has the same role here: it denotes the object a, but with type Y
instead of A.
To understand the necessity of the parameter y, consider the case where the
method body b has result type Y. This method body cannot soundly return an arbitrary
object of type A, because the type A may be shorter than the true Self type of the object
a. Since the object a itself has the true Self type, the method could soundly return it, but
the typing does not work out because a has type A rather than Y. To allow the object a
to be returned, it is bound to y (in the operational semantics of update) with type Y.
This mechanism is critical in certain examples discussed in Section 16.5.
The operational semantics is given in terms of a reduction judgment, f- a-v. The
results of reductions are objects of the form obj(X=A)[li=~(xi:X)bi i<1.."]. The reductions
are essentially untyped, but they propagate the type information contained in terms in
such a way that a subject reduction theorem can hold .
Operational semantics
(Red Object) (where v =obj(X=A)[li=<;(xj:X)bi i<1"))
f-v-v
These rules involve substitutions for the self parameters: (Red Select) replaces self
and (Red Update) replaces old self. They involve also type substitutions to account for
the presence of type variables in objects, replacing the formal Self with the actual Self.
The rules for the judgments E f- 0, E ~ A, and E ~ A <: B are standard, except of
course for the new rules for object types.
Environments, types, and subtypes
(Enyl/S) (Eny x) (Eny X<:)
E~ A x~dom(E) E f- A X~dom(E)
(Sub Object) (where A'" Obj(X)[l,Vi:B;/Xj iEI.."+m ), A' '" Obj(X)[l,Vi': Bi' {Xj iEI.."])
The formation rule for object types (Type Object) requires that all the component
types be covariant in Self. This condition, which has already been discussed in Chapter
15, will be essential in the subject reduction proof.
The subtyping rule for object types (Sub Object) says, to a first approximation, that
a longer object type A on the left is a subtype of a shorter object type A' on the right.
Because of the variance annotations, as in Section 8.7, we use an auxiliary judgment for
inclusion of attributes with variance, and three auxiliary rules. According to the rules,
we may match an invariant component of A against a component of arbitrary variance
of A '; in this sense, an invariant component of A can be regarded as covariant or as con-
travariant. A covariant component of A requires a covariant component of A', and a
contravariant component of A requires a contravariant component of A'. The compo-
nents are considered under the assumption that Self is a subtype A.
The type Obj(X)[ ... j can be viewed as an alternative to the recursive type I1(X)[ ... j,
but with differences in subtyping that are crucial for object-oriented applications. The
subtyping rule for object types (Sub Object), with all components invariant, reads:
'liiEl..n+m
E f- Obj(X)[lj:Bj{X} jE1..n+m] <: Obj(X)[lj:Bj{X} jE1..n]
The rule (Val Object) can be used for building an object of a type A from code for
its methods. In that code, the variable X refers to the Self type; in checking the code, X
is replaced with A, and self is assumed of type A. Thus the object is built with knowl-
edge that Self is A. The methods can exploit that knowledge. For example, a method
with result type Self may be defined to return a fixed object of type A, rather than self
or a modification of self. Such a method is not parametric in Self.
The rule (Val Select) treats method invocation, replacing the Self type X with a
known type A for the object a whose method is invoked. The type A might not be the
true type of a. The result type is obtained by examining a supertype A' of A.
The rule (Val Update) requires that an updating method work with a partially
unknown Self type Y, which is assumed to be a subtype of a type A of the object a being
modified. Because of this requirement, the updating method must be parametric in
Self: it must return self, the old self, or a modification of these. Again, the result type is
obtained by examining a supertype A' of A.
The rules (Val Select) and (Val Update) rely on the structural assumption that
every subtype of an object type is an object type. In order to understand them, it may
be useful to compare them with the following more obvious alternatives:
(Val Non-Structural Select) (where A :; Obj(X)[I,'Ui:Bi{X) iEl.n])
These rules are easy special cases of (Val Select) and (Val Update) for A == A'. Those
rules are more general in that they allow A to be a type variable. For example, if E f- x :
X and E I- X <: Obj(Y)[I:Y), then (Val Select) yields E f- x.l: X, while (Val Non-Structural
Select), together with subsumption, yields only E f- x.l : Obj(Y)[I: Y). The difference is
essential to satisfy the assumptions of either (Val Update) or (Val Non-Structural
Update).
We now have a complete system. Examples of its use appear in Section 16.5.
16.3 Quantifiers
Our system with Self types, S, is sufficient for expressing many examples. However,
this system still lacks facilities for type parameterization. Type parameterization has
228 PART II. SECOND-ORDER CALCULI
useful interactions with objects; in particular, it supports method reuse and inherit-
ance. To allow for parameterization, we add standard bounded universal quantifiers.
We do so without perturbing the existing definitions. We call S\I the resulting system.
First we extend the syntax of types and terms:
Syntax of type parameterization
A,B: := types
(as before)
\f(X<:A)B bounded universal type
a,b ::= terms
(as before)
A(X<:A)b type abstraction
a(A) type application
We add two rules to the operational semantics. According to these rules, evalua-
tion stops at type abstractions and is triggered again by type applications. We let a type
abstraction A(X<:A)b be a result.
Operational semantics for type parameterization
(Red Fun2) (w here v'" A(X<:A)b)
(Red Appl2)
f- b "'"" A(X<:A)c{Xj f- cM '»-- v
f- b(A ') vo+ v
Proof
By induction on the derivation of E f- C' <: C:
In the (Sub Refl) case we have C' == C and from (Type Object) E, X<:Top f- Bi(X}. We
can then use Lemma 16.4-1 to replace Top with C', and apply (Sub Refl). The (Sub
Object) case is also easy, using reflexivity in the case 'U/ == 0. The (Sub Top) case is
immediate. The (Sub X) and (Sub All) cases are vacuous.
For (Sub Trans) we have E f- C' <: c n and E f- c n <: C. By induction hypothesis (on
E f- C' <: cn), either C" == Top or C" has the form Obj(X)[l;\J;":B;"(X} iEK]. In the
former case, C == Top by Lemma 16.4-3, and the proof is complete. In the latter case
we obtain, in addition: K ~ I and, for jEK, if V;"E(O,+}, then V/E(O,+} and E, X<:C' f-
B/(X} <: B;"(X}, and if 'U;"E(O,-}, then 'U/E(O,-} and E, X<:C' f- B;"(X} <: B/(X}. By
induction hypothesis (on E f- C n <: C), either C == Top or C has the form
Obj(X)[li'Ui:Bi(X} iE/]. In the former case, the proof is complete. In the latter case we
obtain, in addition: T~ K and, for JET, if VjE (o,+), then v;" E(O,+) and E, X<:C n f- B;"(X}
<: Bj(X}, and if VjE(O,-}, then 'U;"E(O,-} and E, x< :c n f- Bj(X} <: B;"(X} . Since E f- C' <:
cn, Lemma 16.4-1 yields that, for JET, if 'UjE(o,+}, then 'U;"E (O,+) and E, X<:C' f- B;"{X}
<: Bj(X}, and if 'UjE(O,-}, then V;"E (O,-) and E, X<:C' f- Bj(X} <: B;"{X} . By transitivity,
T ~ I and, for JET, if VjE {o,+}, then Vj' E{O,+} and E, X<:C' f- B;"(X} <: Bj{X}, and if
VjE (O,-), then 'U/E(O,-} and E, X<:C' f- Bj{X} <: B;'(X}.
o
Lemma 16.4-5
If C' == V'(X<:A')B' and E f- C' <: C, then either C == Top,
or C == V'(X<:A)B with E f- A <: A' and E, X<:A f- B' <: B.
Proof
By induction on the derivation of E f- C' <: C.
In the (Sub Refl) case we have C' == C; the derivation must have come from (Type
All<:) with E, X<:A f- B, and therefore we have E f- A by an easy lemma. By (Sub
Refl)' we obtain E f- A <: A and E, X<:A f- B <: B. The (Sub Top) and (Sub All<:)
cases are immediate. The (Sub X) and (Sub Object) cases are vacuous. For (Sub
Trans) we have E f- C' <: c n and E f- c n <: C. By induction hypothesis, either c n ==
Top, and then C == Top by Lemma 16.4-3; or C n == V'(X<:An)B n with E f- An < : A' and
E, X< :An f- B' <: Bn. In the latter case, again by induction hypothesis, we have that
either C == Top, or C == V'(X<:A)B with E f- A <: An and E, X<:A f- Bn <: B. By (Sub
Trans), E f- A < :A'. By Lemma 16.4-1, E f- A <: An implies E, X<:A f- B' <: Bn. By
(Sub Trans), E, X<:A f- B' <: B.
o
16. PRIMITNE COVARIANT SELF TYPES 231
The final lemma establishes that the syntactic criterion for variance is a sufficient
condition for variant behavior:
Lemma 16.4-6
Assume B(Y+,X-j.
If E, X<:A f- B{X,XH and E f- C1 <: C2 and E f- C2 <: A, then E f- B{C1,C2H <: BHC2-CIH.
o
The proof of this lemma is given in Appendix C.2.
We can now prove the subject reduction property.
Theorem 16.4-7 (Subject reduction)
If j1l f- a : A and f- a -- v, then j1l f- v : A.
Proof
By induction on the derivation of f- a -- v.
Case (Red Object) (where v == obj(X=A)[li=~(xi:X)bi iE!.n))
Immediate, since a == v.
Case (Red Select) (where v' == obj(X=C)[li=~(xi:X)b;(X,xij iE !.n))
f- c -- v' f- bjIC,v'» -- v jEl..n
f- c.lj -- v
By hypothesis j1l f- c.lj : A. This must have come from (1) an application of (Val
Select) with assumptions j1l f- c : C and j1l f- C <: D where D has the form Obj(X)[l,vr
Bj{Xj, ... Jand VjE to, +}, and with conclusion j1l f- c.lj : Bjfe», followed by (2) a number
of subsumption steps implying j1l f- BjHe» <: A by transitivity.
By induction hypothesis, since j1l f- c : Cand f- c -- v', we have jZI f- v' : C.
Now JIl f- v': C must have come from (1) an application of (Val Object) with assump-
tions JIl, Xi:C f- MC» : Bi'HCH and C == Obj(X)[liv;':Bi'{X\ i€l ..nJ, and with conclusion
JIl f- v' : C, followed by (2) a number of subsumption steps implying JIl f- C <: C by
transitivity. By transitivity, jZI f- C <: D. Hence by Lemma 16.4-4 we must have
V/E{o,+\ and jZI, X<:C f- B/{Xj <: Bj{X\. By Lemma 16.4-2, jZI f- B/HC'H <: BjHC'l From
j1l, xrC f- bjlC,xjH : B/HCH we obtain JIl f- bjHC,v'H : B/HCH by Lemma 16.4-2. By
Lemma 16.4-6 and the covariance of Bj{X\, we have JIl f- BjHC'H <: BjHCl and so j1l f-
B/HCH <: A by transitivity. By subsumption, JIl f- bjHC',v'H : A.
By induction hypothesis, since j1l f- bjHC,v'H : A and f- bj(C,v'H -- v, we have jZI f- v : A.
232 PART II. SECOND-ORDER CALCULI
Immediate, since a == v.
Case (Red Appl2)
f- b v+ A(X<:D ')cIX} f- c(D"i v+ v
f- b(D") v+ v
By hypothesis fIl f- b(D") : A. This must have come from (1) an application of (Val
AppI2<:) with assumptions fIl f- b : 'V(X<:D)C{X} and fIl f- D" <: D and conclusion fIl
f- b(D") : C{D"&, followed by (2) a number of subsumption steps implying !IS f-
«D"B <: A by transitivity.
By induction hypothesis, since !IS f- b : I;t(X<:D)C{XI and f- b -- A(X<:D')c{X), we
have fIl f- A(X<:D')c{XI : I;t(X< :D)C\X}.
Now, jIl f- A(X<:D')c{XI : 'V(X<:D)c\XI must have come from (1) an application of
(Val Fun2<:) with assumption !IS, X<:D' f- clXI : C{XI and conclusion fIl f-
A(X<:D')C{XI : I;t(X<:D')C{XI, followed by (2) a number of subsumption steps
16. PRIMITIVE COVARIANT SELF TYPES 233
16.5 Examples
We now revisit some examples in order to demonstrate the power of the rules with
primitive Self. In other chapters we have seen how to program these examples, some-
times with fairly elaborate techniques. In contrast, the code in this section is almost as
straightforward as could be expected. The examples show that much can be done wi th-
out quantifiers, but that quantifiers are needed to factor out methods as generic proce-
dures. Later we describe how collections of generic procedures are organized into
classes and subclasses.
For simplicity we use only invariant components. It requires little extrapolation to
see how variance annotations could be used in examples.
In the examples to follow we frequently use the notations of Chapters 7 and 8 as
abbreviations:
[/tUi:Bi iEl..n] ~ Obj(X)[li"Ui:Bi iEl..n) for X;.FV(Bi), iEl..n
[li:Bi iEl..n] ~ Obj(X)[lio:Bi iEl..n) for X;'FV(B i ), iEl..n
[li=<;(xi:A)bi iEl..n) ~ obj(X=A)[li=<;(xi:X)b i iE l.. n) for X;.FV(b i ), iE l..n
a.lj;,<;(x:A)b £ a.lj:;:(Y<:A,y:Y)~(x:Y)b for Y,y;'FV(b)
We also assume basic types and function types.
rr
l1l, x:St, n:Nat I- x : St by (Val x)
111, x:St, n:Nat ~ St <: St by (Sub Refl)
111, x:St, n:Nat, Y<:St, y: Y, z: Y ~ n : Nat by (Val x)
111, x:St, n:Nat ~ x.get*(y)<;(z)n : St (Val Update)
111, x:St ~ A(n)x.get*(y)<;(z)n : Nat-7St -7 introduction
111 ~ [get=<;(x)O, set=<;(x)A(n)x.get*(y)<;(z)n) : St (Val Object)
Note, in particular, the application of the (Val Update) rule, whose assumptions are
easy to satisfy.
More interesting uses of (Val Update) are required when updating methods that
return a value of type Self. We update the method set with a parametric method that
always sets get to o. We prove:
st.set*(Y<:St,y: Y)<;(x: Y)A(n:Nat)x.get:=O : St
as follows:
111 ~ st : St by previous derivation
111 ~ St <: St by (Sub Refl)
rr
l1l, Y<:St, y:Y, x:Y, n:Nat ~ x: Y by (Val x)
111, Y<:St, y: Y, x: Y, n:Nat ~ Y <: St by (Sub X)
111, Y<:St, y:Y, x:Y, n:Nat, Z<:Y, z:Z, w:Z ~ 0 : Nat Nat introduction
111, Y<:St, y:Y, x:Y, n:Nat ~ x.get*(z)<;(w)O: Y (Val Update)
111, Y<:St, y:y, x:Y ~ A(n)x.get*(z)<;(w)O : Nat-7 Y -7 introduction
111 ~ st.set*(y)<;(x)A(n)x.get*(z)<;(w)O : St (Val Update)
The parametricity of the updating method is established by the first application of (Val
Update), which gives type Y to x.get:=O for an arbitrary Y <: St.
The treatment of other kinds of cells is completely analogous. For example, we can
redefine the types Cell and ReCeli of Section 15.2.6:
Cell !
Obj(Self )[contents:Nat, get:Nat, set:Nat-7Self J
ReCeli !
Obj(Self )[contents:Nat, get:Nat, set:Nat-7Seif, backup:Nat, res tore: Self J
We obtain the subtypings ReCell <: Cell <: St.
In the presence of bounded quantifiers, we can define "parametric pre-methods"
as polymorphic functions that can later be used in updating. For example, the method
used above for updating st.set can be isolated as a function of type V(X<:St)X-7X. We
derive:
A(X<:St)A(st:X)st .set*(Y<:X,y: y)<;(x: Y)A(n:Nat)x.get:=O : V(X<:St)X-7X
16. PRIMITIVE COVARIANT SELF TYPES 235
as follows:
Sil, X<:St, st:X f- st: X by (Val x)
f[
Sil, X<:St, st:X f- X <: St by (Sub X)
Sil' X<:St, st:X, Y<:X, y:Y, x:Y, n:Nat f- x : Y by (Val x)
Sil, X<:St, st:X, Y<:X, y:Y, x:Y, n:Nat f- Y < :St by (Sub X, Sub Trans)
Sil,X<:St,st:X,Y<:X,y:Y,x:Y,n:Nat,Z<:Y,z:Z,w:Z f- 0: Nat Nat introduction
Sil, X<:St, st:X, Y<:X, y:Y, x:Y, n:Nat f- x.get;.(z)~(w)O : Y (Val Update)
Sil, X<:St, st:X, Y<:X, y:Y, x:Y f- A(n)x.get;.(z)~(w)O : Nat--t Y --t introduction
Sil, X<:St, st:X f- st .set;,(y)~(x)A(n)x .get;.(z)~(w)O: X (Val Update)
Sil, X<:St f- A(st)st. set;,(y)~(x)A(n)x.get;.(z)~(w)O : X--tX --t introduction
Sil f- A(X<:St)A(st)st.set;,(y)~(x)A(n)x .get;.(z)~(w)O : V'(X<:St)X--tX (Val Fun2<:)
Note, in particular, the essential use of the structural subtyping assumption in the
occurrences of (Val Update) in this derivation; without the structural sub typing
assumption, we would obtain at best the type V'(X<:St)X--tSt instead of the type
V'(X<:St)X--tX.
Polymorphic functions such as the one above are the foundation on which classes
can be built, as shown in Section 16.6.
r SIl' s:Bk f- s : Bk
[ SIl, s:Bk f- Bk <: Bk
SIl, s:Bk, Y<:Bk, y:Y, x:Y f- y: Y
by (Val x)
by (Sub Refl)
by (Val x)
SIl, s:Bk f- s.retrieve ~ (Y<:Self, y:Y) <;(x:Y) y : Bk (Val Update)
SIl, s:Bk f- obj(Self=Bk)[baekup = <;(s:Self) s.retrieve ~ (Y<:Self, y:Y) <;(x:Y) y, ... ] : Bk
(Val Object)
P2 ~ Obj(Seif)[x,y:lnt, mv_x,mv-y:lnt-7Seifl
origin2 ~ obj(Se1f=P2)[x = 0, mv_x = c;(s:Seif) inc_x(Se/J)(s), y = 0, mv-y = .. 1
Moreover, we can define other polymorphic functions of type 'tI(Seif<:P I ) Self-7lnt-7
Self, and use them for updating mv_x in any object of a subtype of PI .
inc2_x : 'tI(Seif<:P1 ) Seif-7lnt-7Seif ~
'A(Seif<:PI ) 'A(s:Seif) 'A(dx:lnt) s.x := s.x+2*dx
originl.mv_x ~ (Seif<:h s':Seif) c;(s:Seif) inc2_x(Seif)(s)
origin2.mv_x ~ (Self<:P2J s':Seif) c;(s:Seif) inc2_x(SelJ)(s)
16.6.1 Classes
Several types of the form 'tI(X<:A)X-7B/X} were used in the previous examples for
updating methods that return values of type Self. These types can be used also for
defining classes as collections of pre-methods. Each pre-method must work for all pos-
sible subclasses, parametrically in Self, so that it can be inherited and instantiated to
any of these subclasses. This is precisely what a type of the form 'tI(X<:A)X-7B/X}
expresses. In the special case where B{X} is simply X, the type 'tI(X<:A)X-7X captures
exactly that the pre-method must return its self argument, possibly with some updates.
Therefore the type 'tI(X<:A)X-7X is a formal version of the condition of being paramet-
ric in Self (first discussed in Section 9.6), and the type 'tI(X<:A)X-7B/X} is a generaliza-
tion of that condition.
As in previous chapters, we can associate a class type Class(A) with each object
type A. We make the components of Class(A) invariant, for simplicity:
A == Obj(X)[liui:Bi/X } i€Lnl
Class(A) ~ [new:A, li:'tI(X<:A)X-7Bi{X} i€Lnl
c : Class(A) ~ [new=c;(z:Class(A»obj(X=A)[Ii=C;(s:X)z .li(X)(s) i€Ln], ... 1
For example, we may rewrite the class of one-dimensional points from Section
15.6.3:
PI ~ Obj(SelJ)[x:lnt, mVJ:lnt-7SeIJl
Class(P I) ~
[new:PI,
x:'tI(Seif<:P I ) Seif-7lnt,
mv]'tI(Se1f<:PI) Se/f-7lnt-7SeIJl
238 PART II. SECOND-ORDER CALCULI
prclass : Class(P 1 ) ~
»
[new = c;(z:Class(P 1 obj(Selj=P1)
[x = c;(s:Se/f) z.x(Self)(s),
mv_x = c;(s:Self) z.mv_x(Self)(s)],
x = A(Selj<:P1 ) A(s:Self) 0,
mv_x = A(Selj<:P1) A(s:Self) A(dx:lnt) s.x := s x. +dx]
We can now consider the inheritance relation between classes. Suppose that we
have another type A' <: A, and a corresponding class type Class(A'):
A' == Obj(X)(liu;':B;'{X) ie1..n+m]
Class(A') == (new:A', li:'It(X<:A')X-+Bi'{X) iel ..n+m]
We say that:
Ii is inheritable from Class(A) into Class(A ')
if and only if X <: A' implies B;{X) < : B;'{X), for all iEl..n
When Ii is inheritable, we have:
'It(X<:A)X-+B;{X) <: 'It(X<:A')X-+B;'{X)
So, if c: Class(A) and Ii is inheritable, we obtain C.li: 'It(X<:A ')X-+B;'{X). Then C.li can be
reused when building a class c' : Class(A '). That is, it can be inherited.
For example, mv_x is inheritable from Class(P 1 ) to Class(P2):
P2 ~ Obj(Se/f)(x,y:lnt, mv_x,mv-y:lnt-+Se/f]
Class(P2) ~
(new:P2,
x,y:'It(Selj<:P2) Self-+lnt,
mv_x,mv-y:'It(Self<:P2) Self-+lnt-+Self]
P2-class : Class(P2) ~
»
(new = c;(z:Class(P2 obj(Se/f=P2)( ... j,
x = A(Selj<:P2) A(S:Self) 0,
y = A(Selj<:P2) A(s:Self) 0,
mv_x = Pl-class.mv_x,
mv-y = A(Se/f<:P2) A(s:Se/f) A(dy:lnt) s.y := s.y+dy]
The inheritability condition holds for any invariant component Ii, since in this case
Bi{X) == B;'{X).
For contravariant components, if A' == Obj(X)W:B'{X), ... ] and A == Obj(X)(t:B{X),
.. .] with A' <: A, then by Lemma 16.4-4 we have that X <: A'implies B{X) <: B'{X). Now,
if c :Class(A), then c.l can always be inherited into Class(A') because 'It(X<:A)X-+B{X)
<: 'It(X<:A')X-+B'{X). Thus inheritability of contravariant components is guaranteed.
Covariant components do not necessarily correspond to inheritable pre-methods.
For example, if A' == W :Nat] and A == W :lnt], and c :(new:A, 1:'It(X<:A)X-+lnt], then c.1
16. PRIMITNE COVARIANT SELF TYPES 239
The result type of fresh is the Self type X, because the new object that fresh generates has
the same type as a.
In order to generate objects of type A with the expected behavior, we need to
extend our representation of classes. We give a special treatment to fresh: in defining
classes for A, we do not include a pre-method for fresh, but instead insert the method
fresh directly into the body of new. We obtain the class type:
C t, [new:A, Ii:'v'(X<:A)X--.?Bi{XI ie1..n)
The code for fresh uses the self of the class, and is well-typed because in the context of
the definition of the new method it is known that the Self type of A is A itself. The pre-
methods of c take as input a type X <: A and a self variable of type X, and hence they
are allowed to call fresh .
If we have a type A' with more methods than A, then we obtain A' <: A by virtue
of the subtyping properties of Self types. Hence pre-methods can be inherited in sub-
classes, by the usual mechanisms. A pre-method that contains an invocation of sjresh
generates in each subclass an object of that subclass.
It is instructive to consider implementing fresh with a regular pre-method, and to
see why this idea does not work. The obvious code for the fresh pre-method is:
fresh=r;(z:Ciass(A»)A.(X<:A)A.(s:X)z.new
Given the type A:; Obj(X)[fresh:X, Ijvj:BilXI jEl..n], our standard definition of Ciass(A)
says that the pre-method should be of type 'V(X<:A)X~X. The code for fresh, on the
other hand, yields only the type 'V(X<:A)X~A. This difference explains why we have
resorted to a special treatment and extended our representation of classes.
17 IMPERATIVE CALCULI WITH SELF TYPES
In this chapter we study an imperative object calculus with primitive covariant Self
types. It is an extension of the first-order calculus of Chapter 11, enriched with the Self
types, the variance annotations, and the quantifiers of Chapter 16. A subject reduction
result covers subtyping and polymorphism in the presence of side-effects.
This chapter combines the ideas of Chapters 11 and 16 in a mostly straightforward
manner, but we do encounter some interesting new issues. The proof of subject reduc-
tion is harder than that of Chapter 11, essentially because of Self types and polymor-
phism. In addition, even the choice of syntax is somewhat delicate: we find that the
interaction of Self types and imperative semantics calls for a generalized form of
method update.
The construct for method update is an (untyped) extension of that of Chapter 16.
In the term a . I~(y,z=c{y})~(x)b{x,y,z}, the variable y still stands for the old self (that is, a),
and may occur free in c and b; the variable x stands for self. In addition, method update
evaluates the term c and binds the result to the variable z, which may occur free in b.
Thus in the untyped imperative calculus of Chapter 10, we may paraphrase:
a.l;o(y,z=c )~(x)b as let y=a in let z=c in y . l;o~(x)b
These terms have the same operational behavior, but the new update construct has a
more powerful typing rule than that which can be obtained from this encoding.
The usefulness of this form of method update will not become clear until an exam-
ple in Section 17.4. Until then, one may think of the new update construct as a combi-
nation of the old update and let constructs.
The new syntax does not include let; as shown later, let is definable in terms of the
extended form of method update.
An update operation a.lj;o(y,z=c)~(x)b first reduces the object a to the final result [li=Li
i<1..nj. Next, with y bound to [li=Li i<1..nj, it reduces the term c to a result v. Finally, it
updates the appropriate store location with a closure. The closure consists of the new
method ~(x)b and a stack binding y and z.
Terms
(Val Subsumption) (Val x)
E I- a : A E I- A <: B E', x:A, E" I- <)
The new rule for update, (Val Update), requires the term c to be typed in an envi-
ronment where y has type Y and where Y is a subtype of the type of the object being
updated. The type C obtained for c is used as the type of z in typing the new method
body b.
The rules (Val Select) and (Val Update) are structural, as in Chapter 16, and so is
the rule (Val Clone).
17.3 Quantifiers
We now extend the impc;-cakulus with quantifiers. We add two new forms to the syn-
tax of terms: we write AOb for a type abstraction and aO for a type application. In typed
calculi, it is common to find A(X<:A)b and a(A) instead. However, we are already com-
mitted to an untyped operational semantics, so we strip the types from those terms.
Technically, we adopt AOb, instead of dropping 1..0 altogether, in order to distinguish
the elements of quantified types from those of object types. The distinction greatly sim-
plifies case analysis in proofs.
244 PART II. SECOND-ORDER CALCULI
(Red Fun2)
0.5 I- <>
0.5 I- AOb -- (AOb,5).0
(Red Appl2)
0.5 I- a -- (AOb,5').0' 0' ·5' I- b -- v·o"
0·5 I- aO -- v·o"
The type rules for polymorphism are the same as in functional calculi, except for
the change in term syntax.
Quantifier rules
(Type All<:) (Sub All)
E, X<:A I- B E I- A' <: A E, X<:A' I- B <: B'
E I- V(X<:A)B E I- V(X<:A)B <: V(X<:A')B'
17.4 Examples
Many of our standard examples carry over to this calculus without surprises. Here we
discuss some peculiarities of imperative semantics and Self types.
We now show by an example that, using just this construct and let, we cannot typecer-
tain terms that we would expect to type.
Consider the type of objects with backup:
Bk £ Obj(Self)[retrieve:Self, backup:Self, ... J
The functional code for the backup method, from Section 6.5.2, is:
backup = r;(self) selfretrieve ~ r;(w) self
This code does not work imperatively: the backup method should make a copy of self,
so that the backup is not affected by side-effects on self. Instead we could write one of:
backup = r;(self) selfretrieve ~ (y) r;(w) c/one(self)
backup = r;(self) self.retrieve ~ (y) r;(w) c/one(y)
Neither achieves the desired effect because cloning is delayed by the r;(w) binder: clon-
ing happens when the retrieve method is invoked, not when the backup method is
invoked. We should therefore pull the cloning outside the binder:
backup = r;(self) let z = c/one(self) in self. retrieve ~ (y) r;(w) z
This code has the expected imperative semantics, but does not typecheck according to
the rule (Val Update'). In typing selfretrieve~r;(w)z, we have self : Bk, and z : Bk as well.
The update requires that for an arbitrary Y <: Bk we be able to show that z : Y. This can-
not be achieved.
Using the more general update construct, we can write the correct code for backup
without relying on let:
backup = r;(self) selfretrieve ~ (y, z=c/one(y» r;(x) z
Moreover, this code typechecks according to the rule (Val Update) of Section 17.2. In
typing self.retrieve ~ (y, z=c/one(y» r;(x) z, we have self: Bk. For an arbitrary Y <: Bk, the
update rule assigns to y the type Y. Therefore we have also c/one(y) of type Y by (Val
Clone) and hence z has the required type Y.
Note that these typing issues arise even with field update, and are not a conse-
quence of allowing method update. They arise from the combination of Self and eager
evaluation when a component of a type that depends on Self is updated .
St ~ Obj(X)[get:Nat, set:Nat.-?X)
st: St ~ [get = 0, set = <;(x) A.(n) x.get := n)
It is natural to expect both components of a storage cell to be protected against
external update. The get field should be protected because it is used only for reading
(since writing should be through set), and the set method should not normally be
updated. For protection, we can use covariance annotations:
ProtectedSt ~ Obj(X)[get+:Nat, set+:Nat.-?X)
We obtain St <: ProtectedSt.
Thus any storage cell can be subsumed into ProtectedSt and protected against
updating. However, the set method is typed with the type St for self, so it can still
update the get field.
Given these definitions, the judgments involving store typing are a fairly straight-
forward generalization of the ones in Section 11.4. To preserve the untyped character
of the operational semantics, type stacks are not included in closures along with stacks;
in particular, (Store Typing) deals with an empty type stack. Then, substitutions A {~T}
must be applied appropriately, as shown in (Stack x Typing) and (Stack X Typing).
Store typing
M ::= Obj(X)[Ii'Uj:Bj jE1..')~j method type (jE1..n)
1: ::= Lj ....M j jEt... store type (Lj distinct)
1:1(L) ~ Obj(X)[lj'Uj:Bj{X} jE1...) if 1:(L) = Obj(X)[lj'Uj:Bj{X} jEl .. n)~j
1:2(A,L) ~ B;{AI if 1:(L) = Obj(X)[Ii'Uj:Bj{X} jE1 .. nl~j
1= ME Meth well-formed method type judgment (M closed)
1:1=¢ well-formed store type judgment
1:l=v:A result typing judgment (A closed)
1:1= S.T: E stack typing judgment (dom(S)vdom(T) = dom(E»
1:1=0 store typing judgment
(Result Object)
1: 1= ¢ 1:1(Lj) == Obj(X)[I,"lJi:l:2(X,Li) iE1..n) ViE l..n
l: 1= [lj=Lj jE1..n) : Obj(X)[I;'Ui:l:2(X,Li) iE1.. nl
17. IMPERATIVE CALCULI WITH SELF TYPES 249
(Result Fun2<:)
~I= S.fIS: E E, X<:A f- b: B/X}
~ 1= (AOb,S) : \t(X<:A)B/X}
(Stack ~ Typing)
~I=<>
(Stack x Typing)
~I=S.T:E ~l=v : A/f-T} x,.dom(E)
~ 1= (S, x>-+v). T : (E, x:A)
(Stack X Typing)
~I=S.T:E flSf-B<:A/f-T} X,.dom(E)
~ 1= S.(T, XI-+B) : (E, X<:A)
(Store Typing)
~ 1= Sj.f/l: Ej Ej, Xj:~l(Lj) f- bj : ~(~l(Lj),Lj) \tiEl..n
~ 1= Ljl-+(C;(xj)bj,Sj) jE 1...
o
Lemma 17.5-2
Assume that ~ 1= S. T : E, then:
(1) XEdom(E) if and only if XEdom(T).
(2) ~ 1= S.fIS: E/f-T} .
(3) If E, E' f- 53, then (E, E'){f-T) f- ~/~T).
o
250 PART II. SECOND-ORDER CALCULI
By Lemma 17.5-2(3), if l: 1= S.T: E and E, E' f- 5, then (E, E'){f-T) f- 5{f-T}. By Lemma
17.5-2(1), l: 1= S.T : E implies that E{f-T} has no type variables; therefore if 5{f-T} is not
of the form a: C, then 16, E'{f-T} f- 5{f-T} by Lemma 17.5-1(2). The proof of the subject
reduction theorem uses Lemmas 17.5-2(3) and 17.5-1(2) in this fashion several times.
The next lemma is the analogue of Lemma 11.4-1; as in Chapter 11, 1;' ~ 1; means
that l:' is an extension of l:.
Lemma 17.5-3
If 1; 1= S. T : E and l:' 1= 0 with l:' ~ 1;, then 1;' 1= S. T : E.
then there exist a closed type At and a store type l:t such that
l:t ~ l: A l:t 1= at A dom(ot) = dom(l:t) A l:t 1= v: At A 16 f- At <: A{f-T}.
Proof
The proof is by induction on the derivation of o.S f- a -- v.ot. Despite its complex-
ity, it contains essentially no surprises.
Case (Red x)
o.(S', XI-+[I;=l; ;'l..nj, S") f- 0
(Val Object), for some type Obj(X)[/iVi:BiIX} i<1..I,) such that E I- Obj(X)[/;\Ji:BiIX}
i<I..II) <: A . Therefore E, xi:Obj(X)[/;\Ji:BiIXI i<1..111 I- bi : Bi(Obj(X)[/ivi:BiIX} i<l.. nlB for
iEl..n.
Since we have E I- Obj(X)[/iVi:BiIX} i<!..n) <: A, we must also have E I- Obj(X)[/iVi:
BilX} if 1..11), and therefore E, X<:Top I- B;/X+} for iEl..n and X~dom(E). Since
X~dom(E), we have X~dom(T) by Lemma 17.5-2(1). Hence jIl, X<:Top I- BiIX}{f- T}
by Lemmas 17.5-2(3) and 17.5-1(2). Take At == Obj(X)[/;\Ji:BiIX} iEl.II){f-T}. There-
fore, since jIl, X<:Top I- BiIX}{f-T}, (Method Type) yields 1= At~j E Meth for jEl..n.
Take I:t == I:, Lj ..... (At~j)j<l..lI; by (Store Type) we have I:t 1= 0, because Lj~dom(o), and
hence Lj~dom(I:), and because 1= A t ~j E Meth for jE 1.. n.
(t) Since I:t is an extension of I:, we also have I:t 1= S. T: E by Lemma 17.5-3, so I:t 1=
S.jIl: Elf-T} by Lemma 17.5-2(2). From E, xi:Obj(X)[/;\Ji:BiIX} i<I..II) I- bi : Bi(Obj(X)
[liVi:BiIX} i<!..II1» we have Elf- T}, xi:A t I- bi : Bilf-TJHA t, by Lemma 17.5-2(3), that is,
Elf- T}, Xi:I:tl(Li) I- bi : I:tZ(I:tl(Li),Li).
(2)We have that 0 is of the form Ek .....(~(Xk)bk,Sk} kE!..m. Now I: 1= 0 must come from the
(Store Typing) rule, with I: 1= Sk.jIl: Ek and Ek, Xk:I:I(Ek) I- bk: I:Z(I:I(Ek),Ek). By Lemma
17.5-3, I:t 1= Sk.jIl : Ek; moreover Ekt Xk:I:tl(Ek) I- bk : I:t 2(I: t I(Ek),Ek), because I:t(Ek) = I:(Ek)
for kEl..m since dom(o) = dom(I:) = IEk k<I..III} and I:t extends I:.
Take ot == (0, Li ..... (~(Xi)bi,S) i<1 .. II). By (1) and (2), via the (Store Typing) rule, we have
I:t 1= ot. Since I:t 1= 0 and I:t == I:, Ljl-+(A t~j) j<l..lI, we have I:t 1= [/i=Li i<1 .. II) : At by the
(Result Object) rule.
We conclude that I:t ~ I: /\ I:t 1= ot /\ dom(ot) = dom(I: t ) " I:t 1= [/i=li i<I..II) : At /\ jill-
At <: A{(-- T}.
Case (Red Select)
0.5 I- a v-+ [/i=li i<l..lIl.o' o'(Lj) = (~(xj)bj,5') x,~dom(5') jE l..n
0' .(5', xj ..... [/i=li iEI..II)) I- bj v-+ v.o"
0.5 I- a.lj v+ v.o"
A' <: D{f-T} by transitivity. By Lemma 16.4-4 we must have 1!/E{o,+} and fIl, X<:A'
~ l:'2(X,Lj) <: Bj{XIIf-T} with U-dom(T). Hence fIl ~ l:'2(A',Lj) <: Bj«A'Hf-T} by
Lemma 17.5-1. Then, from Ej, x/l:' I(Lj) ~ bj : l:' 2(l:' 1(Lj),Lj), that is, from Ej, xj:A' ~ bj :
l:'2(A',Lj), we obtain Ej, xj:A' ~ bj : Bj«A'Jlf-T} by subsumption. Moreover, from L' 1=
S'.fIl: Ej and L' 1= [Ii=Li iE1..") : A' we get L' 1= (5', xt... [li=Li iE1..")).fIl: (E j, xj:A') by (Stack
X Typing).
Let E' == Ej, xj:A'. By induction hypothesis, since E' ~ bj : Bj«A'&!f-T} A 0'.(5',
xj ..... [li=Li iE1..")) ~ bj -- v.o" 1\ L' 1= 0' 1\ dom(o') = dom(l:') 1\ L' 1= (5', xj ..... [li=Li iE1..")).fIl
: E', there exist a closed type At and a store type Lt such that Lt ),:: l:' A Lt 1= 0" 1\
),:: l: 1\ l:' 1= 0' 1\ dom(o') = dom(l:') 1\ l:' 1= [li=Li iEI.."): At 1\ fIl ~ At <: A'{f-T}.
Now, l:' 1= [li=Li jE1.."] : At must have been derived via (Result Object) from L' 1= 0
and l:'1(Lj) == Obj(X)[lj1!j':l:'2(X,Lj) jE1.."] == At for all iEl..n. We have fIl ~ A'{f-T} <:
D{f-T} by Lemmas 17.5-2(3) and 17.5-1(2), and then fIl~ At <: D{f-T} by transitivity.
17. IMPERATIVE CALCUU WITH SELF TYPES 253
Hence by Lemma 16.4-4 we must have V/E(O,-} and fIS, X<:At I- Bj(X}(~T} <:
I;' 2(X,lj), for Xtdom(T). Therefore, fIS I- Bj{AtH~T} <: I;' 2(A t ,lj) by Lemma 17.5-1.
Let T' == T, Y>-+At. From I; 1= S.T : E we have I;' 1= S.T: E by Lemma 17.5-3. Then
from fIS I- At <: A'(f-T} we obtain I;' 1= S.T': (E, Y<:A') by (Stack X Typing). Then
from I;' 1= [li=li iEl..n) : At == Y(~T'} we obtain I;' 1= (S, y>-+[li=li iEl..n)).T': (E, Y<:A',
y: Y) by (Stack x Typing).
By induction hypothesis, since E, Y<:A ', y :Y I- c: C /\ a' .(S, y>-+[li=li iEl..n)) I- c -- v-o"
/\ I;' 1= a' /\ domea') = dom(I;') /\ I;' 1= (S, y>-+[li=li iEl..n))_T': (E, Y<:A', y: Y), there exist
a closed type C t and a store type I;t such that I;t ~ l:' /\ I;t 1= 0" /\ dom(o") = dom(I;t)
/\ I;t 1= v: C t /\ fIS I- C t <: C(~T'}.
Take ot == o"V-«C;(x)b, (S, y>-+[li=l; iEl..n), z>-+v».
• I;t ~ I; by transitivity.
• We have dom(I;t) = dom(a'). Moreover dom(a')!;;;; dom(o") by the definition of reduc-
tion, and hence ljEdom(a"). So ot is well-defined, and dom(a") = dom(ot). Therefore
dom(I;t) = dom(ot).
• By (1), (2), and (3) below, we obtain I;t 1= ot via the (Store Typing) rule.
(1) From I;' 1= S-T: E we have r 1= S.fIS: E(~T} by Lemma 17.5-2(2). From l:' 1= [li=li
iEJ..n) : At we have I;' 1= (S, y ..... [li=li iEl..n))_fIS : (E(~T}, y:At) by (Stack x Typing). By
Lemma 17.5-3 we have I;t 1= (S, y>-+[li=li iEl..n)).fIS: (E(~T}, y:At). From I;t 1= v: C t we
have I;t 1= (S, y>-+[li=li iEl..n), z>-+v).fIS: (E(~T}, y:A t , z:C t ) by (Stack x Typing).
(2) From I; 1= S.T: E and E, Y<:A', y:Y, z:C, x:YI- b: Bj we obtain E(~T}, Y<:A'(f-T},
y:Y, z:C(~T}, x:Y I- b : Bj(~T} by Lemma 17.5-2(3). Since fIS I- At <: A'(~T} and fIS I-
C t <: C(f-T'} we have E(~T}, y:A t , z:C t , x :At I- b: Bj{~T'} by Lemma 17.5-1(4). Since
flSl- Bj(At»!f-T} == Bj(~T'} <: r 2(A t ,lj) we have E(~T}, y:A t , z:Ct , x:At I- b: I;'2(A t ,lj)
by subsumption. That is, E(~T}, y:A t , z:C t , X:I;\(lj) I- b : l:t2(I;\(li),li).
(3) Since I;t 1= a" must come from (Store Typing), 0" has the shape Ek'-"(c;(xdbJc,Sk)
kEl..m, and for all k such that Ek ~ lj and for some Ek we have I;t 1= Sk-fIS : E", and EJc,
xk:I;t 1(Ek) I- bk : I;t2(I;t1(Ek),Ek).
• From I;' 1= [li=li iEl..n) : At and I;t ~ I;', by Lemma 17.5-3 we have I;t 1= [li=li iE1..n) : At.
• By Lemmas 17.5-2(3) and 17.5-1(2) we have fIS I- A'(~T} <: A(~T}, so by transitivity
fIS I- At <: A(f-T}.
We conclude that l:t ~ I; /\ I;t 1= ot /\ dom(I;t) = dom(ot) /\ I;t 1= [li=li iEl..n) : At /\ fIS I-
At <: A(f-T}.
Case (Red Clone) (li'distinct)
o.S I- a ~ [li=li iEl ..n)_o' liEdom(a') l;,tdom(o') ViEl .. n
o.S I- clone(a) ",... [li=l;' iEl..n)_(a', l;' ..... a'(li) iEl..n)
254 PART II. SECOND-ORDER CALCULI
Take At == V(X<:C)B and 1;t == 1;. We conclude 1;t ~ 1; 1\ 1;t 1= a 1\ dom(a) = dom(1;t) 1\
that 1;' ~ 1; 1\ 1;' 1= cr' 1\ dom(cr') = dom(1;') 1\ 1;' 1= (AOb,S') : A' 1\ 16 f- A' <:
(V(X<:D)B)/f-T).
Now 1;' 1= (AOb,S'): A' must come from (Result Fun2<:), with A' == V(X<:D')8', 1;' 1=
5'.16: E', and E', X<:D' f- b: B'. We have also 16 f- D/f-T} <: D' and 16, X<:D/f-T} f- B'
<: B/t-T} since 16 f- A' <: (V(X<:D)B)/t-T), by Lemma 16.4-5.
Take T == 16, X..... C/f-T}. We have l:' 1= 5'.16: E' and 16 f- C/f-T} <: D' by transitivity.
Therefore, 1;' 1= S'.T: (E', X<:D') by (Stack X Typing).
By induction hypothesis, since E', X<:D' f- b : B' 1\ cr' .5' f- b -- v.a" 1\ 1;' 1= a' 1\
dom(cr') = dom(l:')1\ 1;' 1= S'.T: (E', X<:D'), there exist a closed type At and a store
type 1;t such that 1;t ~ l:' 1\ 1;t 1= a" 1\ dom(a") = dom(1;t) 1\ 1;t 1= v : At 1\ 16 f- At <:
B'/f-T'}.
We have (B'{X})ff-T') == B'(C{f-TlD by definition of T', hence 16 f- At <: B'[C(f-T}R.
Since 16 f- C(f-T} <: D{f-T} and 16, X<:D{f-T} f- B'/X} <: B{X}{f-T}, we have 16 f-
B'IC/f-TlJ <: B(C{f-TlHf-T} by Lemma 17.5-1. Since E f- B[q <: A, we have 16 f-
(B{q){f-T) <: A/f-T} by Lemmas 17.5-2(3) and 17.5-1(2). Finally 16 f- At <: A{f-T}
by transitivity.
We conclude 1;t ~ 1; 1\ 1;t 1= a" 1\ dom( a") = dom(1;t) 1\ 1;t 1= v : At 1\ 16 f- At <: A {f-T}.
o
We have also an analogue to Corollary 11.4-4:
Corollary 17.5-5
If 16 f- a : A and 16.16 f- a -- v.a, then there exist a type At and a store type 1;t such that
1;t 1= a and 1;t 1= v: At, with 16 f- At <: A.
o
This corollary implies, for example, that if 16 f- a : A and 16.16 f- a -- v.a where A is
an object type, then v is an object result of the form [lj=Lj jE1..II] and never a type abstrac-
tion result of the form (AOb,S).
18 INTERPRETATIONS OF OBJECT CALCULI
Now that we have investigated several object calculi in much detail, we are in a good
position to address a basic question:
Why should we study object calculi, instead of encoding objects in A-calculi?
In order to answer this question, we describe several techniques for encoding objects,
resuming the discussion of Section 6.7. These techniques provide interesting interpre-
tations of objects. Still, we argue, these interpretations are not a replacement for object
calculi.
tation: interpretations differ in their complexity, in the range of features they can
model, and in the convenience of expressing examples.
A faithful interpretation of object calculi is one that preserves all judgments, in a
formal sense. For typed calculi, in particular, we expect the preservation of subtyping
judgments. That is, if a subtype relation exists between types of an object calculus, then
a corresponding subtype relation should exist between the interpretations of those
types in a A-calculus with subtyping.
It is easier to find interpretations of object calculi that do not preserve subtyping.
These interpretations map uses of the subsumption rule to the insertion of coercion
functions [2). Such interpretations are unsatisfactory in two respects. First, they fail to
explain object subtyping in terms of more primitive subtype relations (e.g., in terms of
record subtyping). Second, by introducing cumbersome coercion functions in place of
subsumption, they fail to preserve the flavor of the original programs.
Therefore we strive to find interpretations that preserve subtyping. This turns out
to be a difficult task that stimulates the development of sophisticated techniques. The
difficulty of the task, in a sense, justifies the study of object calculi as separate systems.
18.1.2 Features
To understand the full scope of the problem of finding interpretations, and to learn
potentially useful techniques, it is important to discuss interpretations that do not quite
work. As a consequence, we do not always define the interpretations formally; further-
more, the interpretations generally fail to validate some of the rules of object calculi.
We discuss a number of interpretation techniques. Most of these can handle only
restricted forms of our object calculi, for example calculi that distinguish fields from
methods or that forbid method update. As a guide, we list below the syntactic forms
that are handled (to different degrees) by the various interpretations. These tables are
only an approximation: each interpretation handles a specific syntax with specific
restrictions. Informally, we categorize operations as internal when they act on self (or
an updated self) in the context of an object definition, and as external otherwise. Some
interpretations deal differently with the two kinds of operations, and some deal only
with one kind. Other interpretations treat these two kinds of operations uniformly.
We consider the following extended syntax for object calculi with separate fields
and methods:
Syntax for separate fields and methods
[fk;Bk kEJ..m I li: Bi i.J..nJ object type with m fields and n methods
[jk=bk kEl .. m I li=~(xi :A)b;{xi} i.l..nJ object with m fields and n methods
oAof field selection
OAof=b field update
OA.l method invocation
o . I~~(y: A)b method update
18. INTERPRETATIONS OF OB]ECf CALCULI 259
The subscripted term OA indicates that 0 has type A; we omit the subscript when this
information is not needed. For untyped interpretations, we consider versions of this
syntax where all type information has been erased. For imperative interpretations, we
consider an additional cloning primitive.
When considering object types with Self, we write types and terms of the following
forms:
In Obj(X)[fk:Bk kE1..m I li:B;/X+1 iE1..H], the variable X occurs only covariantly in the Bi;
moreover, X does not occur in the Bk and bk.
When m = 0, the syntax in the previous two tables yields calculi with no fields (or,
more precisely, calculi where fields are encoded as methods). This special case corre-
sponds to our standard syntax with types of the form [/i:Bi iE1..n] or Obj(X)[li:BiIX+1 iEI .. H]
and objects of the form [/i=<;(Xi:A)b;{Xi} iEI..H] or obj(X=A)[/i=<;(xi:X)biIX,xil iEI..H].
18.2.1 Self-Application
The simplest interpretation of object calculi is the one that implements method invoca-
tion as self-application [73, 74]. The self-application interpretation maps objects to
records of functions. On method invocation, a whole object is passed to a method as a
parameter. In a functional context, we have:
Here we distinguish fields from methods because, imperatively, they call for different
evaluation strategies.
The occurrences of init in the translation of update are bound to the closest enclosing
definition of init. Therefore only internal update is handled correctly. The translation
of an external update would leave init unbound, or would bind it to the wrong defini-
tion. Subject to these limitations, this interpretation models object calculi with private
fields over which only internal updates are allowed.
18.2.3 State-Application
The state-application interpretation is a variant of the self-application interpretation
[103]. When considering calculi that support only field update, it becomes natural to
treat fields and methods separately. The fields can be grouped into a state record st,
separate from the method suite record mt. Methods receive st as a parameter on
method invocation, instead of the whole object as in the self-application interpretation.
The update operation modifies the st component and copies the mt component. The
method suite is bound recursively with a J.I., so that each method can invoke the others.
Thus we obtain the following interpretation:
Untyped state-application interpretation
I
(fFbk kEI .. m I li=~(xi)bi iEI .. n] ~ (J/(, Ii distinct)
(st=(/k=bk kEl..m), mt=J.I.(m)(li=A(S)b/ iEI .. n)) (for appropriate b/)
o,/j ~ o.st./j (jE l..m) (external)
oo/j:=b ~ (st=(o.st./j:=b), mt=o·mt) (jEt .. m) (external)
o.lj ~ o.mt.lj(o.st) (jE 1..n) (external)
There is also a more subtle difficulty. Methods that return self should produce a
whole object, but the available self parameter contains only fields: a whole object,
including a method suite, must be regenerated at the right time. The burden of regen-
eration can be placed either on the methods of the object, or on the user of the object.
In the first case, methods produce structures of the form (st=s', mt=m), where 5' is a
modified state, and m is the recursively defined method suite. In the second case, the
user of an object writes code such as the following:
let s = o·st and m = o·mt
in let 5' = ... s ... m.lj ...
in (st=s', mt=m)
The object 0 is decomposed into its state 5 and method suite m; then various internal
operations are performed using 5 and m, obtaining a new state 5'; finally a new object
is assembled from 5' and the initial m.
In either case, the code produced for methods that return self is rather different
from the code produced for methods that do not return self. Although it is fairly clear
how to translate specific examples, it is quite hard to describe formally an interpreta-
tion that works, especially in an untyped context. A proper formulation, however, can
be found by letting types drive the translation; see Section 18.3.3.
When the translation is performed correctly, the state-application interpretation
implements our primitive semantics of objects, except for the omission of proper
method update. One advantage of this rather laborious encoding is that it corresponds
to some extent to the behavior of class-based languages, where method suites are kept
separate from state records.
The create function takes a collection of functions and produces a record. Amethod
Ijis updated by supplying the new code for Ij to the corresponding function Ir
d; this
18.3.1 Self-Application
A typed version of the interpretation of Section 18.2.1 is obtained by representing
object types as recursive record types:
Self-application interpretation
A:; [li:Bi iE!..n) ~ (Ii distinct)
J.l.(X){li:X-+Bi iE1..n}
Unfortunately, the subtyping rule for object types fails to hold: note that a contra-
variant X occurs in all method types. If we ignore this serious problem, it is easy to
extend the self-application interpretation to Self types, by setting Obj(X)[li:Bi{X) iE1..n] ~
J.l.(X){li:X-+B;lXllE!..n}. Similarly, the interpretation can be extended with cloning as in
Section 18.2.1.
18. INTERPRETATIONS OF OBJECf CALCULI 265
This refined interpretation validates the subtyping rules for object types because the
type variable X occurs only covariantly in Il(X)(/k:Bk kEl .. m, li:Bi{X) iEl ..n).
This interpretation is frequently used to code object-oriented behavior within "'-
calculi with records, for specific examples. A typical application concerns movable
color points:
CPoint g,
Obj(X)[x:lnt, c:Color I mv :lnt~X)
cPoint : CPoint g,
Ix = 0, c = black I mv = ~(s : CPoint) ",(dx:lnt) sox := soX+dx)
The translation is:
CPoint g,
Il(X) (x:lnt, c:Color, mv:lnt~X)
266 PART II. SECOND-ORDER CALCULI
cPoint : (Point ~
let rec init(xO:lnt, cO:(olor):(Point =
l1(s:CPoint) fold( (Point,
(x = xO, c = cO,
mv = t..(dx:lnt) init(unfold(s).x+dx, unfold(s).c»)
in init(O, black)
This translation achieves the desired effect, yielding the expected behavior for cPoint
and the expected subtypings for CPoint. In this example, the correct binding of init does
not pose any problem. In contrast, if the code for mv had been t..(dx:lnt)j(s).x := s.x+dx,
wherefis a function of the appropriate type, it would not have been clear how to give
the translation.
18.3.3 State-Application
As a first attempt at typing the state-application interpretation of Section 18.2.3, we can
set:
[fi,:Bk kEI ..m I hB; ;'1..n) ~
(st:(Jk:Bk kel..m), mt:(I;:(/k:Bk kel..m)--7B; ;el..n»
Unfortunately there is a subtyping problem here because of the contravariance of the
method argument types, just as in the self-application interpretation.
A better solution can be found via existential types (see Section 13.2 for notation).
Interestingly, recursive types are not needed. The resulting interpretation handles
object calculi with Self types that do not support method update. A full description of
this technique is, however, not straightforward. We sketch the main idea here, and we
refer to [71, 72, 103) for details.
The state of an object, represented by a collection of fields st, is hidden from view
by existential abstraction, hence external updates are not possible. The troublesome
method argument types are hidden as well, so this interpretation yields the desired
subtypings.
State-application interpretation
A;;: Obj(X)[I;:B;{X+} ;'1..n) ~ (Ii distinct)
3(X)C{X}
where C{X} ;;: (st:X, mt:(I;:X--7B;{X+) ;'1..n»
obj(X=A)[!k=bk kel..m I 1;=<;(x;:X)b;lx;} ;El..n) ~ (!k, I; distinct)
(pack X=(/k:Bk kEl..m)
with (st=(!k=bk kEl..m),
mt=l1(m:(I;:X --7B; ;.l..n»(I;=t..(s:X)b;' ;E1..n» (for appropriate b;')
: ({X})
x;oh ~ s·h (jEl .. m) (internal)
18. INTERPRETATIONS OF OBJECT CALCULI 267
name evaluation, but it can be easily adapted for call-by-value evaluation.) In sum-
mary, it respects both the type rules and the primitive semantics of simple objects.
However, this interpretation is essentially incompatible with imperative seman-
tics, because every method invocation creates a fresh object instead of reusing the cur-
rent one. Furthermore, it does not extend to object types with Self because any
occurrence of Self in a type Bi appears contravariantIy within the type of the corre-
sponding component l;,'pd. For these reasons, we go on to consider a more sophisticated
version of the split-method interpretation.
A third idea is to go back to our initial type for l/el and to use the Self-quantifier
technique for obtaining covariance:
[li:Bi ieLn) ~ J.1(Y)3(X<:Y)(l/el:X~Bi ieLn, ltpd:(X~Bi)~X iEl..n)
This encoding validates the subtyping rules for object types because all the occurrences
of X, bound by 3, are covariant. Unfortunately, with this encoding it is impossible to
perform method invocations: after opening the existential type we do not have an
appropriate argument of the abstract type X to which to apply the l;sel components.
Since that argument should be the object itself, we can solve this problem by adding a
record component, r, that is bound recursively to the whole record; this record compo-
nent is analogous to the recoup method of Section 15.6.
This solution involves a simple change in the untyped interpretation and yields the
following typed interpretation [9):
Split-method interpretation
A =[li:Bi ieLn) ~ (Ii distinct)
J.1(Y)3(X<: Y)C{X)
where qX) = (r:X, lrl:X~Bi ieLn, l/,pd:(X~Bi)~X ieLn)
[li=~(xi:A)bi ieJ..n) ~
let rec create(Yi:A~Bi iel..n):A =
fold(A,
pack X=A
with
(r=create(Yi ie Ln),
lrl=Yi ieLn,
ltpd='A(y;':A~Bi)create(Yjjel..i-t, Yi', Yk kei+Ln) ieLn)
: qX))
in create('A(xi:A)bi ieLn)
oA.lj ~ (jEl..n)
open unfold(o) as X<:A, p:C{X) in p.l/,/(p.r): Bj
o.lj~~(y:A)b ~ (jEl..n)
open unfold(o) as X<:A, p:C{X) in p.lrd('A(y:A)b) : A
270 PART II. SECOND-ORDER CALCULI
With this interpretation, we obtain both the expected semantics and the expected
subtyping properties. This interpretation validates all the typing, subtyping, and
reduction rules for simple objects, up to Chapter 8. It also validates the equational rules
except for (Eq Sub Object), which is not derivable but is consistent. Moreover, it can be
extended in a number of ways, as we show next.
Variance annotations can be added with little extra effort. An invariant attribute,
1°, is translated to two record components Isel and lupd as above; a covariant one, Z+, is
translated to just the Isel component; a contravariant one, 1- , is translated to just the lupd
component. This extended interpretation validates the subtyping rules for variance
annotations.
Since the interpretation uses the Self-quantifier idea, it is natural to extend it to Self
types, as follows:
Split-method interpretation (with Self types)
i A == Obj(X)[li:B;/X} iE l..nj ~ (Ii distinct)
Jl(Y)3(X<:Y)C{X}
where C{X} == (r:X, Ir':X~B;{X) iE1..n, Irpd:(X--7BiIX))~X iE1..n)
obj(X=A)[li=C;(Xi:X)bi iE1..nj ~
let rec create(Yi:A --7BiIA } iE1..n):A =
fold(A,
pack X=A
with
(r=create(Yi iE l..n),
l/e1=Yi iEl..n,
1;"pd=A(Yi':A~B;/A))create(YjjE1..i-l, Yi', Yk kEi+1..n) iE1..n)
: CIX))
in create(A(xi:A)bi iEl..n)
oA.lj ~ (jEl..n)
open unfold(o)as X<:A, p:CIX} in p.l/"'(p .r): BjiAR
o.lj~(X< :A,y : X)C;(x:X)bly} ~ (jEl..n)
open unfold(o) as X<:A, p:C{X} in p.lrd(A(X:X)b{p.rD: A
oA,!i:=b ~ (jel..m)
open unfold(o) as X<:A, p:C{X}
in fold(A,paek X'<:X=X with p'!i:=b: «X'a> : A
oA.lj ~ open unfold(o) as X<:A, p:C{X} in p.lip·r) : Bj (je l..n)
o.ljfF~(x:A)b ~ (je l..n)
open unfold(o) as X<:A, P:C{X}
in fold(A,paek X ' <:X=X with p.lr='A.(x:A)b : qx'}) : A
clone(oA) ~ open unfold(o)as X<:A, p:C{X} in p.cl«»: A (jel..n)
As presented here, the interpretation is for a first-order imperative calculus with clon-
ing (the first-order version of the calculus treated in Section 18.2.1). Much as in the
split-method interpretation, we could also handle Self types.
18.3.7 Discussion
In sum, we have described a few techniques for translating typed object calculi into
typed calculi without objects. Some of the techniques deal with rich object calculi that
include, for example, method update, cloning, and (to some extent) Self types. How-
ever, there is not yet a simple, definitive interpretation that once and for all explicates
the meaning of objects.
Given the difficulty in finding a definitive interpretation, object calculi provide a
useful abstract view of objects and a basis for understanding object-oriented languages,
independently of particular encoding techniques.
19 A SECOND-ORDER LANGUAGE
In this chapter we apply our second-order calculi to the study of a simple but expres-
sive programming language. This language, called 0-2, is similar to the language 0-1
considered at the end of Part I. Like 0-1, 0-2 includes both class-based and object-
based constructs.
0-2 illustrates the new possibilities that come with second-order typing and with
Self types. Essentially, 0-2 provides satisfactory subtyping for objects with methods
that return self, and inheritance for those methods. It is more sophisticated than 0-1 in
these respects.
Much as we did for 0-1, we translate 0-2 into an object calculus. In this case the
object calculus is Sit (Chapter 16), which features primitive covariant Self types and
bounded universal quantifiers. The translation reduces the rules for 0-2, which are
fairly elaborate, to the simpler rules of the object calculus.
19.1 Features
0-2 preserves most of the features of 0-1 and introduces a few new ones. At first sight
0-2 may appear almost identical to 0-1, but there are significant differences in their
typing rules. We have adopted similar notations for 0-1 and 0-2 in order to emphasize
how the "same" constructs may have subtly different meanings in different object-ori-
ented languages.
Like 0-1,0-2 includes both class-based and object-based features. Objects have
embedded methods, which can be updated. Subclasses can be created by single inher-
itance; methods of superclasses may be overridden and specialized. Interfaces are sep-
arate from implementations, and inheritance is separate from subtyping. Sub typing
supports subsumption.
The main new feature of 0-2, with respect to 0-1, is Self types. In 0-1, the variable
X of an object type Object(X)[lj'\Jj:Bi/X} jEl..nj is a recursion variable; our interpretation
of 0-1 maps Object(X)[ljuj:Bi/X} jELnj to fl(X)[Ij\Jj:Bi/X} iELnj. In contrast, in 0-2, the
variable X is the Self type, in the sense of Chapters 16 and 17; our interpretation of 0-2
maps Object(X)[ljuj:Bj{X} iEl..nj to Obj(X)[ljuj:Bj{X} iEl..nj.
Because of Self types, some subtypings that do not hold in 0-1 do hold in 0-2, for
example Object(X)[lj:x' 12:lntj <: Object(Y)[lj :Yj. On the other hand, 0-2 is not a
proper extension of 0-1. For example, Object(X)[I:X ---7lntj is a legal type in 0-1 but not
in 0-2, because contravariant occurrences of Self types are not permitted. In principle,
we could have mixed recursive types and Self types in order to make 0-2 a proper
extension of 0-1, but this combination could have been confusing.
As the meaning of object types changes, so does the meaning of class types. In 0-2,
a class for Object(X)[li\!i:Bi{X} i€!.."] has a method Ii that maps a self parameter of type
X to a result of type Bi{X\, for an arbitrary subtype X of the object type A. Thus, the
method must be parametric in Self. The same parametricity requirement applies to the
code used for overriding a method of an object of type Object(X)[I;\Ji:B;/X} i€!.."] from
the outside.
Because of the new parametricity requirement, it becomes harder to write the code
of methods. For example, in 0-1, the method I of a class for Object(X)[I:X] may return
a fixed object of type Object(X)[I:X], which is easy to construct. Instead, in 0-2, the
method must return a value of type X for an unknown X <: Object(X)[I:X]; this value
can be constructed only using the self parameter. The difficulty in defining methods is
compensated by an increase in reusability. In particular, it is simple to inherit methods
that return values of type Self, because these methods are parametric in Self.
In addition to Self types, 0-2 introduces bounded universal types in support of
polymorphism. The technical concepts needed for this are already present because of
the parametricity requirement on methods, and hence the inclusion of bounded poly-
morphism is straightforward.
The introduction of Self types and polymorphism removes the need for many uses
of typecase. For simplicity, we do not include typecase in 0-2. As the examples of this
chapter show, however, typecase would still be useful in some circumstances, in par-
ticular for handling binary methods.
19.2 Syntax
The following table gives the syntax of 0-2 types; these are similar to 0-1 types, with
the addition of bounded universal types:
Syntax of 0-2 types
A,B::= types
X type variable
Top the biggest type
Object(X)[li\!i:Bi if!.."] object type (each Bi covariant in X)
Class(A) class type
All(X<:A) B bounded universal type
As in 0-1, we omit basic types (such Int) and function types, but we use them in
examples. Top is the biggest type. An object type Object(X)[li\!i:B;{X} i€1."] has as a Self
type X, labels Ii, variance annotations \!i (-, 0, or +), and attribute types B;{X} where X
occurs only covariantly in each B;{X}. A class type Class(A) is a type for classes gener-
ating objects of type A, where A is an object type. A bounded universal type
All(X<:A)B{X} is the type of an abstraction that for any subtype A ' of A produces a
result of type B«A 1
19. A SECOND-ORDER LANGUAGE 275
The notion of covariance for these types is analogous to that defined in Chapter 16.
We omit a definition, and note only that Class(A) is invariant in all the type variables
that occur free in A.
The syntax of terms is given next. Most constructs are similar to the ones of 0-1,
and have roughly the same meaning. One of the syntactic differences is in the variable
binders for object, method, and subclass, where x:X<:A is short for x:X and X<:A, and
x:X=A is short for x:X and X=A. Once again, the syntax could be interpreted function-
ally or imperatively.
Syntax of 0-2 terms
An object can be written as object(x:X=A) li=b;/X,x} iE1..n end, where x is the self
variable for all the attributes. The binding X=A is just a convenience; it gives a name X
to the Self type A within the object. The object type A must be known (must not be a
type variable). For an imperative semantics, it is important to distinguish fields from
proper methods. As in 0-1, we keep the syntax simple by assuming an unspecified
syntactic criterion for this distinction; fields should not contain occurrences of the self
variable.
Methods that are used for update must be parametric in Self; thus the method
update construct has a binder x:X<:A, where x is self and X is the Self type. For simplic-
ity, we do not include a parameter representing the old self (Section 16.2.2).
In the subclass construct, the header c:C with(x:X<:A) identifies a superclass c, its
class type C (having, say, n components), and the type A of the objects generated by the
subclass (having n+m components). The binder introduces a self variable x, and a Self
type variable X, with the assumption that X is a subtype of A. Both the object type A
and the class type C must be known (must not be type variables).
The class selection construct c"I(A,a) extracts the method named I from c and
applies it to the type A and the term a. The type A must be a subtype of the object type
276 PART II. SECOND-ORDER CALCULI
corresponding to c, and a must have type A. As in 0-1, class selection provides the
functionality of super; in 0-2, an occurrence of cAl(X,x) as part of the code for a sub-
class of c with self variable x and with Self type X corresponds to a use of super.
Bounded polymorphic abstraction and application are the standard constructs of
Chapter 13.
As in 0-1, we define abbreviations for the Root class type, for direct subclasses of
root, for super, and for object-based inheritance:
Root t,
Class( Object(X)[))
class with(x:X<:A) li=bi i€J..n end t,
subclass of root:Root with(x:X<:A) li=b i i€J..n override end
subclass of c:C with(x:X<:A) .. . super.l . .. end t,
subclass of c:C with(x:X<:A) .. . cAl(X,x) ... end
object(x:X::::A) .. . I copied from c ... end £
object(x:X=A) .. . l=cAl(X,x) ... end
19.3 Examples
In this section we revisit some of our examples. These examples illustrate the features
of 0-2, and its main differences with 0-1.
19.3.1 Points
We start by rewriting the 0-1 examples of Section 12.3, emphasizing the differences.
Those examples concern points and color points, with eq and mv methods. Because of
the covariance restriction, we can no longer have the binary method eq with type
X~Boo/' Instead, we use an auxiliary type Coord that carries sufficient information for
some implementations of eq.
Coord t, Object(X)[x: lntJ
Point t, Object(X)[x: lnt, eq+: Coord~Bool, mv+: lnt~XJ
CPoint t, Object(X)[x: lnt, c: Color, eq+: Coord~Bool, mv+: lnt~XJ
The type of mv in Point is stilllnt~X, but this now has a stronger meaning. The result
type variable X does not stand for Point as in 0-1, but for the Self type. Because of sub-
sumption, the "true type" that Self represents may be any subtype of Point. By the sub-
typing rules (detailed in Section 19.4), we obtain CPoint < : Point.
Corresponding to these two types, we define the classes:
pointClass : Class(Point) t,
class with (self X<:Point)
x =0,
19. A SECOND-ORDER LANGUAGE 277
19.3.2 Cells
As a further illustration of the subclass mechanisms of 0-2, we recode the cell exam-
ples of Section 15.2. The 0-2 code for these examples is not always straightforward, but
it is as direct as we might expect; it is simpler than the corresponding terms of Section
15.2. In addition, there are no uses of typecase.
Cell ~
Object(X)[contents: Nat, get: Nat, set: Nat~Xl
cellClass : Class(Cell) ~
class with(se/f X<:Cell)
contents = 0,
get = selfcontents,
set = fun(n: Nat) self.contents := n end
end
278 PART II. SECONO-OROER CALCULI
ReCell ~
Object(X)[contents: Nat, get: Nat, set: Nat"-7X, backup: Nat, restore: Xl
reCellClass : Class(ReCell) ~
subclass of cellClass: Class(Cell )
with(self X<:ReCell)
backup = 0,
restore = self.contents := self.backup
override
set = fun(n : Nat) cellClassAset(X, self.backup := self.contents)(n) end
end
ReCell' ~
Object(X)[contents: Nat, get: Nat, set: Nat"-7X, restore: Xl
reCellClass': Class(ReCell') ~
subclass of cellClass: Class(Cell )
with(self X<:ReCell')
restore = self.contents := 0
override
set = fun(n: Nat)
let m = self.contents
in cellClassAset(X,
self.restore := method(y: Y<:X) y.contents := mend)
(n)
end
end
end
We have ReCell <: ReCell' <: Cell. In these type definitions, we have not included any
variance annotations; it would be sensible to adorn some components with +, in order
to protect them against overrides, but this is not necessary for the subtyping ReCell <:
ReCell' <: Cell.
Finally, we briefly illustrate the use of polymorphism by writing a generic equality
function and a generic doubling function:
eqPredicate: All(X<:Cell) X"-7X"-7Bool ~
fun(X<:Cell) fun(se/f X) fun(other: X) self.get = other.get end end end
double: All(X<:Cell) X"-7X ~
fun(X<:Cell) fun(x: X) x.set(2*x.get) end end
These functions can be applied directly, or they can be embedded in classes for sub-
types of Cell.
19. A SECOND-ORDER LANGUAGE 279
19.4 Typing
The type rules for 0-2 are based on the following judgments:
Judgments
EI-<> environment E is well-formed
EI-A A is a well-formed type in E
EI- A <: B A is a subtype of B in E
E I- vA <: v'B A is a subtype of B in E, with variance annotations v and v'
El-a:A a has type A in E
The rules for environments are standard, and identical to the ones in 0-1.
Environments
(Env¢) (Env X<:) (Env x)
E I- A X;.dom(E) E I- A x;'dom(E)
E, X< :A I- <> E, x:A I- <>
In the rules for types, the only novelty with respect to 0-1 is the covariance
requirement for object types. (We omit the definition of covariance. A similar definition
is given in Section 16.2; it suffices to add that Class(A) is invariant in all of the type vari-
ables that occur free in A.)
Types
(Type X) (Type Top)
E', X<:A, En I- <> EI-<>
E', X<:A, En I- X E I- Top
(Type Object) (Ii distinct, "iE (0, -:)) (Type Class) (where A == Object(X)[I,wBiIXI iE!..n))
E, X<:Top I- Bi(X+1 'v'iEl..n EI-A
E I- Object(X)[I;'Ui:Bi(XI ie1.. n l E I- Class(A)
Subtyping
(Sub Refl) (Sub Trans) (Sub X) (Sub Top)
EI-A E I- A <: BEl- B <: C E', X<:A, En I- <> EI-A
EI-A <:A E I- A <: C E', X<:A, En I- X <: A E I- A <: Top
280 PART II. SECOND-ORDER CALCULI
The rule (Sub Object) is slightly different from the corresponding rule of 0-1. The
difference is a change in assumption, from E, X<:A' ~ Vi Bi(X} <: Vi' B;'{A') to E, X< :A ~
Vi Bi(X} <: V;' B;'(X} . This change reflects the change in interpretation from 0-1 to 0-2:
from recursive object types to object types with covariant Self. The difference is impor-
tant but subtle; it constitutes a prime example of the issues one encounters in giving
sound typing rules for object-oriented languages, and of the usefulness of developing
a solid foundation" for these rules.
The differences between 0-2 and 0-1 are also evident in the following typing
rules for terms:
Terms
(VaISubsumption) (Val x)
E~a:A E ~ A <: B E', x:A, E" ~ 0
(Val New)
E I- c :Class(A)
E I- newc: A
(Val Root)
EI-o
E I- root: Class(Object(X)[])
(Val Subclass) (where A:; Object(X)[/'-wBi iE1..n+m], A':; Object(X')[I,vi':B;' iE1..n], Ovr!;;1..n)
E I- c' : Class(A') E I- A <: A'
E, X<:A I- Bi' <: Bi 'v'iEl..n-Ovr
E, X<:A, x:X I- bi: Bi 'v'iEOvrun+l..n+m
E I- subclass of c':Class(A') with(x:X<:A) li=b i iEn+J..n+m override li=b i iEOvr end
: Class(A)
The rule for constructing a single object, (Val Object), has no parametricity require-
ment. The object is built with knowledge of the Self type (A), and its methods need
work only with a self argument of this type.
The rules (Val Select), (Val Update), and (Val Method Update) are all structural
rules (see Chapter 16). The rule for field update, (Val Update), guarantees that a new
field is vacuously parametric in Self, by requiring that the Self variable does not occur
in the field type. The rule for method update, (Val Method Update), requires a new
method to be parametric in Self. The rule for selection, (Val Select), exploits this para-
metricity.
Similarly, the rule for building subclasses, (Val Subclass), requires new methods to
be parametric in Self (through the assumption E, X<:A, x:X I- bi : BJ The rule for class
selection, (Val Class Select), exploits this parametricity.
In the rule (Val Subclass), the assumption E I- A <: A' requires that the object type
associated with a subclass be a subtype of the object type associated with its superclass.
The subtyping rules for variance annotations come into play. For invariant components
(often fields) the component type in A must be the same as the corresponding compo-
nent type in N. For covariant components (often proper methods) the component type
282 PART II. SECOND-ORDER CALCULI
19.5 Translation
We relate 0-2 to our calculi via a translation, proceeding much as for 0-1. We show a
translation into the functional calculus 5\1 of Chapter 16; a similar translation could be
given into the imperative calculus of Chapter 17. We write «A» for the translation of the
type A, and «a»
for the translation of the term a.
As announced, the translation of the object type Object(X)[lj\)j:Bj iEJ..n] is
Obj(X)[ljW«Bj» JEJ..n]. The translation of a class type is less straightforward; using the
ideas of Section 16.6, we map a class type Class(Object(X)[Ij\!j:B;/X} jE!..n]) to an object
type that contains components for pre-methods, and now the pre-methods have
bounded universal types. We assume that the label new does not occur in the source
language.
Translation of 0-2 types
«X» ! X
«Top» ! Top
«Object(X)[lj\}j:Bj jE!..n]» ! Obj(X)[lj\)j:«Bj» iEI.n]
«Class(A)>> ! Obj(Z)[new+:«A», It:'v'(X<: <<A>>)X~<<Bj>> iE!..n]
where A == Object(X)[I,'\Jj:Bj jf!..n] and Z~FV(<<A»)uFV(<<Bi» iE1..n)u{X}
«All(X<:A)B» ! 'v'(X<:«A»)«B»
The translation of terms resembles that for 0-1; we add a type annotation to field
update in order to make the translation syntax-directed.
19. A SECOND-ORDER LANGUAGE 283
«x» ~ x
«object(x:X=A) li=b i ie!..n end» ~ obj(X=«A»)[li=<;(x:X)«M ifl..n])
«a.lj» ~ «a».lj
«(a:A).1 := b» ~ «a».l;.(X<:«A»,y:X)<;(x:X)«b»
where y,xtFV(<<b»)
«a.l := method(x:X<:A) b end» ~ «a».l;.(X<:«A»,y:X)C;(x:X)«b»
where ylFV(<<b»)v{x}
«new c» ~ «c».new
«root» ~ obj(Z=Obj(X)[new+:A])[new=c;(z:Z)obj(X=A)[)]
where A == Obj(Y)(J
«subclass of c':Class(A') with(x:X<:A) li=bi ien+l..n+m override li=bi ieOur end» ~
obj(Z=«Class(A)>> )
[new=c;(z:Z)obj(X=«A»)[ li=C;(S:X)z.li(X)(S) ie 1..n+m]
li=C;(Z:Z)«c'».li iel .. n-Our,
li=C;(Z:Z)A(X<:«A»)A(X:X)«M ieOvrvn+ l ..n+m]
where A == Object(X)[I,'Ui:Bi iel..n+m] and A' == Object(X)[I,--u/:B/ if1..n)
and z,ZtFV(<<A»)vFV(<<c'»)vFV(<<M ieOvrvn+1..n+m)v{X}
«c"fj(A,a)>> ~ «c».lj(<<A»)(<<a»)
«fun(X<:A) b end» ~ A(X<:«A»)«b»
«b(A)>> ~ «b»(<<A»)
Finally, we can extend the translation to whole judgments, for example by setting
«E f- a : A» ~ «E» f- «a» : «A».
The translation validates all the rules of 0-2; we prove a similar result in detail in
Chapter 21. Here we give only an informal argument for (Val Subclass), as it gives
insight into many parts of the translation at once.
Let A == Object(X)[f,wBi ie1..n+m] and A' == Object(X)[I,'Ui':B/ ie!..n]. Informally, let us
assume that «c'» : «Class(A')>>, that «A» <: «A'», that «B/» <: «Bi» for all X < : «A» and
iE1..n-Ovr, and that «M: «Bi» for all X <: «A», x:«X», and iEOvrvn+ l..n+m. We need to
show that «subclass of c':Class(A') with(x:X<:A) li=b i ien+1..n+m override li=bi ieOvr end»
: «Class(A)>>. For this purpose, it suffices to check that: (1) for the new method,
obj(X=«A»)[lFC;(S:X) z.li(X)(s) ie1..n+m) : «A» if z:«Class(A)>>; (2) for inherited methods,
«c'»-I;: If(X<:«A»)X-7«Bi» if iE1..n-Ovr; (3) for overridden and fresh methods, A(X<:«A»)
A(X:X) «M : If(X<:«A»)X-7«Bi» if iEOvrvn+ l..n+m . Condition (2) follows from the sub-
typing If(X<:«A'»)X-7«B/» <: If(X<:«A»)X-7«B i».
PART III
Higher-Order Calculi
20 A HIGHER-ORDER CALCULUS
In Part III we study the interaction between objects and higher-order features. In this
chapter, we start afresh from our basic calculus with first-order object types and sub-
typing, Db 1<:. We enrich this calculus with a structural rule for method update and
with variance annotations (introduced in Sections 8.7 and 13.1.3, and studied in Chap-
ters 16 and 17). We put the resulting fragments in the context of a type theory with
higher-order subtyping, based on Girard's F", [42, 62]. The resulting calculus is the
object-oriented counterpart of F<u<~ the higher-order polymorphic A-calculus with sub-
typing [51, 102]. By further adding recursive types, we obtain the calculus Db<U<:1l"
The calculus Db<U<:1l is the main subject of this chapter. After defining Db<u<:J.V we
prove a subject reduction theorem for it, using Compagnoni's approach [51], and then
we develop some examples. As these examples show, Db<U<:1l enables us to capture
some advanced features of object-oriented languages. In particular, Db<U<:1l provides a
general account of inheritance and Self types. It enables us to formalize the notion of
object protocol that we introduced informally in Section 3.5 in order to deal with binary
methods.
This is a function mapping a type X to the type V'(Y<:X)Y. A structure of kinds is intro-
duced to classify types and operators. The kind of all types is called Ty. An operator
from types to types has kind Ty=> Ty. Higher-order operators can be expressed as well,
with kinds such as (Ty=>Ty)=>Ty. In general,K=>L is the kind of the operators mapping
kind K to kind L.
Types and operators are collectively called constructors. Every well-formed con-
structor has a kind; we write A :: K to say that constructor A has kind K. The subtype
relation is generalized to a higher-order relation: the subconstructor (or simply, inclu-
sion) relation. On types, this relation reduces to ordinary subtyping. On operators, it is
defined inductively as pointwise inclusion; that is, B <: B' holds at kind K=>L if for all
A of kind K we have B(A) <: B'(A) at kind L. The inclusion relation is written in full in
the form A <: B :: K; this means that the constructors A and B are both of kind K, and
that A is included in B. For example, A <: Top is written in full as A <: Top :: Ty.
The general form of bounds for constructor variables is X < : A :: K; this means that
X has kind K and is included in the constructor A of kind K. These bounds may appear
in quantified types and their terms: V(X<:A: :K)B is the type of those constructor
abstractions A(X<:A::K)b mapping a constructor X of kind K, included in A, to a term b
of type B. In order to simplify the technical treatment of higher-order features, we
restrict operators to have bounds of the form X: :K. We write A(X::K)B for the operator
that maps X of kind K to B, and we take A(X)B as an abbreviation for A(X::Ty)B. Our
initial example of an operator is written in full as:
A(X::Ty)V(Y<:X::Ty)Y :: Ty~Ty
We postpone giving the type rules of ObOl<:11 to Section 20.3; the syntax of ObOl<:11 is
summarized in the following table:
Syntax of Obc.><:11
K,L ::= kinds
Ty types
K~L operators from K to L
A,B ::= constructors
X constructor variable
Top the biggest constructor at kind Ty
[li\li:Bi i£ I..n] object type (Ii distinct, \liE to, -,+})
V(X<:A::K)B bounded universal type
J.1(X)A recursive type
A(X::K)B operator
B(A) operator application
a,b ::= terms
x variable
[1=<;(xi:Ai)b i i£l..n) object formation (Ii distinct)
a.l method invocation
a.l~<;(x:A)b method update
A(X<:A::K)b constructor abstraction
b(A) constructor application
fo1d(A,a) recursive fold
unfold(a) recursive unfold
Thus kinds are either the kind of types or the kinds of operators. Constructors are
either variables, types (including a maximum type, object types, bounded universal
types, and recursive types), operators, or operator applications. Terms include objects,
constructor abstractions, and recursive folds. All expressions are identified up to
renaming of bound variables.
We do not include primitive existential quantifiers; we can encode these in terms
of universal quantifiers, losing only some equational properties (Section 13.2). Simi-
20. A HIGHER-ORDER CALCULUS 289
larly, we do not include function types, which we can encode in terms of object types
with variance annotations (Section 8.7).
(Red Update)
f- a ..... [lj=C;(xj:Aj)bj jEl..n) jEl .. n
(Red AppI2::)
f- b ..... A(X<:A::K)c{X) f- cIA' } ..... v
f- b(A ') ..... v
290 PART III. HIGHER-ORDER CALCULI
(Red Fold)
f-b- v
f- fold(A,b) -- fold(A,v)
(Red Unfold)
f- a -- fold(A,v)
f- unfold(a) -- v
20.3 Typing
The type rules of ObOl<:Il are formulated in terms of the following judgments:
Judgments for Db_: Il
Ef-o E is an environment
E f- K kind Kisa kind
Ef- A:: K constructor A has kind K
Ef-A ... B :: K A and B are equivalent constructors of kind K
Ef- A <: B :: K A is a subconstructor of B, both of kind K
E f- \JA <: \J'B A is a subtype of B according to variances \J and ,,'
Ef-a:A a is a value of type A
The need for the judgment E f- A - B :: K arises because the presence of operators
entails computation at the constructor level. Because of this, we need to define an equa-
tional theory of constructors that accounts for operator evaluation. This equational the-
ory is analogous to the one for the terms of FlI but lifted to the level of constructors. For
example, the theory implies that (A(X: :Ty)'If(Y<:X::Ty)Y)(Top) equals 'If(Y<:Top::Ty)Y.
On the other hand, there is no judgment for equivalence of terms; we have given an
operational semantics for terms, but will not give a corresponding equational theory.
In this respect we proceed as for previous calculi with structural rules.
Several abbreviations are used extensively throughout Part III:
Notation
rTy 1 ~ Top
rK=:>Ll ~ A(x::KfLl
X::K ~ X <JKl:: K (in environments and some binders)
X<:A ~ X < : A :: Ty (in environments and some binders)
X ~ X <: Top:: Ty (in environments and some binders)
Ef-A ~ E f- A:: Ty
Ef-A-B ~ Ef-A ... B: : Ty
E f- A <: B ~ E f- A <: B : Ty
:
20. A HIGHER-ORDER CALCULUS 291
These abbreviations allow us to omit some bounds and some kinds. According to the
abbreviations, fKl denotes the maximum constructor at kind K.
Using these judgments and notations, we list the inference rules for Ob",<:1V group-
ing them by judgment. The rules are mostly straightforward.
Environment formation
(Envf/l) (Env X< :) (Env x)
E f- A :: K X~dom(E) E f- A x~dom(E)
E, X<:A::K f- 0 E, x:A f- 0
Kind formation
(Kind Ty) (Kind =})
E f- 0 E f- K kind E f- L kind
E I- Ty kind
Constructor formation
(Con X) (Con Top)
E', X<:A::K, En I- 0 Ef-o
E', X<:A ::K, En f- X:: K E f- Top :: Ty
(Con Object) (Ii distinct, \liE (O,-,+ )) (Con All) (Con Rec)
E I- Bi \tiE l..n E, X<:A: :K f- B E, Xf- A
E f- [li'Ui:Bi iE!.."] E I- \t(X<:A::K)B E I- Il(X)A
Constructor equivalence
(Con Eq Symm) (Con Eq Trans)
EI-A ++ B:: K E I- A ++ B :: KEf- B ++ C :: K
Ef- B -A :: K EI-A ++ C:: K
(Con Eq Rec)
E, XrB- B'
E r J.1(X)B ++ J.1(X)B'
Constructor inclusion
(Con Sub Refl) (Con Sub Trans)
ErA-B: : K ErA < : B :: K E r B < : C :: K
ErA<: B:: K ErA<: C:: K
Term typing
(Val Subsumption) (Val x)
E f- a : A E I- A <: B E', x:A, E" f- ¢
(Val Object)
E, Xi:A f- bi : Bi 'ViEl..n E f- A - [I,'Ui:Bi it!.."]
E f- [IFC;(xi:A)bi iE!.."] : A
(Val Select)
E f- a : [liwBi it!.."] VjE (O,+) jE l..n
E f- a.lj : Bj
Note that we adopt a structural rule for method update (the one introduced in Section
13.1.3). The structural rule plays an important role in our applications of Obw<:~ we do
not know whether it can be avoided.
294 PART III. HIGHER-ORDER CALCULI
1\ consLft(U)
B ·
1\ consRht(U)
B ·
Figure 20-1. Binary trees.
The numerals can be seen as unary trees, with zero as the initial root, and with the
successor method adding a new root and a connecting edge to an existing tree. With
two distinct successor functions, we obtain binary trees; a consLft method adds a new
root and a given tree to the left of an existing one, and conversely for consRht (Figure
20-1). The methods consLft and consRht are binary methods in the sense of Section 3.4.
These trees can be coded as follows in ObO><;jl:
Object-oriented binary trees
Bin ~ Il(X)[isLeafBool, lft:X, rht:X, consLft:X-tX, consRht:X-tX]
UBin ~ [isLeafBool, lft:Bin, rht:Bin, consLft:Bin-tBin, consRht:Bin-tBin]
leaf: Bin ~
fold(Bin,
[is Leaf = true,
1ft = c;(self: UBin) self 1ft,
rht = c;(self:UBin) self.rht,
20. A HIGHER-ORDER CALCULUS 295
The operator BinOp is the object protocol of Bin, in the sense of Section 3.5. The type Bin
can be recovered from BinOp by taking a fixpoint. The type UBin, the unfolding of Bin,
is obtained by applying BinOp to Bin.
We can now write the definition of BinClass, which is based on the familiar notion
of parametric pre-methods. The new twist is that the types of pre-methods are quanti-
296 PART III. HIGHER-ORDER CALCULI
fied over the suboperators of BinOp, instead of being quantified over the subtypes of
Bin (which are scarce). Fixpoints must be introduced where appropriate, to collapse
operators down to types:
Object-oriented binary-tree class type
BinClass !
(new+:Bin,
isLeaf:'V(X<:BinOp) X*~Bool,
lft+,rht+:'V(X<.:BinOp) X*~X*,
consLft+,consRht+:'V(X<.:BinOp) X*~X*~X*1
The use of + guarantees that classes cannot be accidentally modified, but is not neces-
sary for the typing of classes.
Writing the raw code for a class of type BinClass is not difficult, but typing such
code requires understanding some subtle issues about what is typable within ObW<:/l"
The following properties, and their derivations, are useful exercises. The final Ex. 5
shows how a binary tree whose type is (partially) unknown can be paired with itself,
yielding a binary tree of the unknown type.
Ex. 1: 9$, X<.:BinOp r X*
[[ J/S,r...
X<.:BinOp, x:X* f- unfold(x) : BinOp(X*)
see Ex. 2 and Ex. 3
by (Val Subsumption)
J/S, X <.:BinOp, x:X* f- unfold(x).consLft : X*-'tX* by (Val Select)
J/S, X<.:BinOp, x:X* f- x : X* by (Env x)
J/S, X<.:BinOp, x:X* f- unfold(x).consLft(x) : X* by (Val AppI)
We are finally ready to write a class that generates objects of type Bin. The coding
of the method new is, as usual, lengthy but straightforward. The code for isLeafis trivial,
and the code for ift and rht relies on Ex. 2 and Ex. 3.
Object-oriented binary-tree class
bin Class : BinClass ~
[new =
c;(z:BinClass) fo1d(Bin,
[isLeaf = C;(s:UBin) z.isLeaj(BinOp )(fold(Bin, s»,
ift = C;(s:UBin) z.ift(BinOp)(fold(Bin, s»,
rht = C;(s:UBin) z.rht(BinOp)(fold(Bin, s»,
consLft = C;(s:UBin) z.consLft(BinOp)(fold(Bin, s»,
consRht = C;(s:UBin) z.consRht(BinOp)(fold(Bin, s»))),
isLeaf = A.(X<.:BinOp) "A(self:X*) true,
ift = "A(X<. :BinOp) "A(self:X*) unfold(self)·lft,
rht = "A(X <.:BinOp) "A(self:X*) unfold(self ).rht,
consLft =
"A(X <.:BinOp) "A(rht:X*) "A(lft:X*)
fold(X*, «unfold(rht).isLeaf := false).lft := lft).rht := rht),
consRht =
"A(X<.:BinOp) "A(lft:X*) "A(rht:X*)
fo1d(X*, «unfold(ift).isLeaf:= false).!ft := ift).rht := rht)]
The most intriguing part of this code is the successful typing of consLft (and of
consRht). We start from unfold(rht) : X(X*), as in Ex. 3. The chain of updates maintains
the typing X(X*), as in Ex. 4, by virtue of the structural update rule. The final fold maps
X(X*) back into X*, which is the desired result type.
We can verify that inheritance of binary pre-methods is possible. For example, for
the pre-method consLft of type 'V(X<.:BinOp)X*-7X*-'tX*, we have:
'V(X <.:BinOp )X*-7X* -7X* <: 'V(X <.:BinOp')X* -7X* -'tX*
for any BinOp' <.: BinOp
A suboperator of BinOp is, for example:
298 PART III. HIGHER-ORDER CALCULI
NatBinOp ~
A(X)[n:Nat, isLeaf:Bool, lft:X, rht:X, consLft:X~X, consRht:X~Xl
so consLft can be inherited into a class built for the type NatBin. In defining such a class,
the pre-method consLft can simply be binClass.consLft.
The rule (Con Sub Refl) of Obw<:J.l is replaced by the simpler (NCon Sub Refl),
which removes the dependence of the inclusion judgment from the equivalence judg-
ment; the judgment E f- A :: K is used instead.
The most interesting rules in the normal system are (NCon Sub X) and (NCon Sub
AppI). Together they handle inclusions of the form X(Aj) .. .(An) <: C, by requiring that
(lubE(X(A j ) ...(An)))n! < : C. All other inclusions of the form B(A) <: C must be validated
by (NCon Sub Refl).
The handling of normal forms is rather delicate. There are no rules in the normal
system able to derive inclusions of terms that are not in normal form. Environments
may contain terms that are not in normal form, but the rules (NCon Sub X) and (NCon
Sub AppI) normalize them whenever they are accessed.
300 PART III. HIGHER-ORDER CALCULI
The main properties of the normal system are its soundness and completeness
with respect to ObW<:11 over normal forms. These properties are expressed in Lemma
20.5-5. Lemmas 20.5-2, 20.5-3, and 20.5-4 are all intermediate results for proving
Lemma 20.5-5.
20. A HIGHER-ORDER CALCULUS 301
Immediate, since a == v.
Case (Red Select) (where v' == [li=~(Xi:Ai)Mxi) iEl .. n])
f- c.lj -- v
By hypothesis ¢ f- c.lj : A. This must have come from (1) an application of (Val
Select) with assumptions ¢ f- c: C with C of the form [ljuj:B j, ... J, ujElo,+\, and with
conclusion ¢ f- c.lj : Bj, followed by (2) a number of subsumption steps implying ¢ f-
Bj <: A by transitivity.
By the first induction hypothesis, since ¢ f- c : C and f- c -- v', we have ¢ f- v' : C.
Now ¢ f- v': C must have come from (1) an application of (Val Object) with assump-
tions ¢, Xi:C' f- bi : B/ and ¢ f- C' H [1{U/:B/ iE1..n), and with conclusion ¢ f- v' : C',
followed by (2) a number of subsumption steps implying ¢ f- C' < : C by transitivity.
Therefore ¢f- [1/ui':B/ iE1..n) <: C by (Con Eq Symm), (Con Sub RefI), and (Con Sub
Trans). Hence by Lemma 20.5-6(3) we must have ¢ f- B/ <: Bj- From ¢, Xj:C' f- bjlxj}
: B/ we obtain ¢ f- bj(v" : B/ by Lemma 20.5-1(3). Also, ¢ f- B/ <: A by transitivity.
By subsumption, ¢ f- bj{v" : A.
By the second induction hypothesis, since ¢ f- bj(v'} : A and f- bj«v'} -- v, we have ¢
f-v:A.
Case (Red Update) (where v == [li=~(Xi:Ai)bi iE1..n])
f- c -- v jE1..n
f- c.lj ~ ~(x:C)b -- [lj=~(x:Aj)b, li=~(Xi:Ai)bi iE(1..n)-/j'J
By hypothesis ¢ f- c.lj ~ ~(x:C)b: A. This must have come from (1) an application of
(Val Update) with assumptions ¢ f- c : C, and ¢ f- C <: D where D has the form
[ljuj:B j, ... ], and ¢, x:C f- b : Bj with UjE 1°,-I, and with conclusion ¢ f- c.lj ~ ~(x : C)b : C,
followed by (2) a number of subsumption steps implying ¢ f- C <: A by transitivity.
By induction hypothesis, since ¢ f- c : Cand f- c -- v, we have ¢ f- v : C.
20. A HIGHER-ORDER CALCULUS 303
Now f/S f- v : C must have come from (1) an application of (Val Object) with assump-
tions f/S, x;:C f- b;: B;' and f/S f- C H [1;\J;':B;' ;e1..n) (hence A; = C for iEl..n) and with
conclusion f/S f- v : C, followed by (2) a number of subsumption steps implying f/S I-
C <: Cby transitivity. Thereforef/Sf- [1;\J;':B;,;e1..n) <: Cby (Con Eq Symm), (Con Sub
Refl), and (Con Sub Trans). By transitivity, f/S f- [ltV;':B;' ;e1..n) <: D. Hence by Lemma
20.5-6(3), we must have f/S I- Bi <: B( From f/S, x:C I- b : Bi and Lemma 20.5-1(1) we
obtain f/S, x:C I- b : Bi, and by subsumption f/S, x:C f- b : B( Then by (Val Object) we
obtain f/S I- [lj=~(x:C) b, 1;=~(x;:C)b; ;e1..n-/iI) : C'. Since f/S f- C <: A by transitivity, we
have f/S I- [lj=~(x:C) b, l;=~(x;:C)b; ;e1..n-/iI) : A by subsumption.
Case (Red AppI2::)
I- b -- A(X<:D'::L')c(X) I- c«D"B -- v
f- b(D") -- v
By hypothesis f/S I- b(D") : A. This must have come from (1) an application of (Val
AppI2<::) with assumptions f/S I- b: 'v'(X<:D::L)ClX) and f/S f- D" <: D:: L and conclu-
sion f/S f- b(D") : qD"H, followed by (2) a number of subsumption steps implying f/S
f- qD"B <: A by transitivity.
By the first induction hypothesis, since f/S I- b : 'v'(X<:D::L)C{X) and I- b ."..
A(X<:D'::L')c{XJ, we have f/S I- A(X<:D'::L')c{X) : 'v'(X<:D::L)C{X).
Now, f/SI- A(X<:D'::L ')c{X) : 'v'(X<:D::L)C{X) must have come from (1) an application
of (Val Fun2::) with assumption f/S, X< :D'::L' I- c{X) : C{X) and conclusion f/S I-
A(X<:D'::L')c{X) : 'v'(X<:D': :L')C{X), followed by (2) a number of subsumption
=
steps implying f/S I- 'v'(X<:D'::L')C{X) <: 'v'(X<:D::L)ClX). Hence L L', f/S f- D < :D'
:: L, and f/S, X<:D::L I- C{X) <: ClX) by Lemma 20.5-6(2).
We have f/S I- D" <: D :: L, and also f/S f- D" <: D' :: L by transitivity. Therefore f/S I-
C(D"B <: qD"l and f/S I- dD"B : CID"B by Lemma 20.5-1(2).
By the second induction hypothesis, since f/S f- cfD"B : C«D"B and I- cHD"B .".. v, we
have f/S I- v : C(D"}, By transitivity and subsumption, II! f- v : A.
Case (Red Fold)
I- jold(C,b)"'" jold(C,v)
Assume that II! I- jold(C,b): A. This must have come from (1) an application of (Val
Fold) with assumptions f/S f- C H Il(X)B{X) and f/S I- b : B«Cl and conclusion f/S f-
jold(C,b): C, followed by (2) a number of subsumption steps implying f/S I- C <: A by
transitivity. By induction hypothesis, we have f/S I- v: BHea. Therefore we obtain s>J
f- jold(C,v): C by (Val Fold), and s>J f- jold(C,v): A by subsumption.
304 P ART III. HIGHER-ORDER CALCULI
Assume that f/l f- unjold(c): A. This must have come from (1) an application of (Val
Unfold) with assumptions f/l f- c : ~(X)B{X) and conclusion f/l f- unjold(c) :
B(~(X)B{Xl&, followed by (2) a number of subsumption steps implying f/l I-
B«~(X)BIXl! <: A by transitivity. By induction hypothesis, we have f/l f- jold(Cv) :
~(X)B{X). Now f/l f- jold(C,v) : ~(X)BIX) must have come from (1) an application of
(Val Fold) with assumptions f/l f- C H ~(X ')B ' IX ' ) and f/l f- v: B'(q, and conclusion
f/l f- jold(C,v) : C, followed by (2) a number of subsumption steps implying f/l f- C <:
~(X)B{X) by transitivity. Therefore f/lf- ~(X')B ' {X ') <: ~(X)B{X) by (Con Eq Symm),
(Con Sub Refl), and (Con Sub Trans).
By Lemma 20.5-6(4), either:
(1) f/l, X f- BIX} H B'iX&; therefore f/l f- ~(X)BIX) H ~(X')B 'IX ') by (Con Eq Rec).
Hence also f/l f- ~(X)BIXI H C. Then by (Con Eq Abs), (Con Eq Appl), (Con Eval
Beta), we obtain f/l f- B«~(X)BIXJ& H B'Kt. Therefore, f/l f- B'te} <: A .
(2) f/l, X, X'< :X I- B'IX'} <: BIX}, and therefore f/l, X'< : ~(X)BIX} I- B'IX'} <:
BMX)BIXJ& and then f/l f- B'(Cj <: BMX)BIXJ& by Lemma 20.5-1(2). By transitivity,
f/l f- B'Ie} <: A.
In either case, by subsumption, f/ll- v : A.
o
21 A LANGUAGE WITH MATCHING
In this chapter we introduce a programming language named 0-3 that supports gen-
eral Self types. The surface features of 0-3 are similar to those of the language 0-1 of
Chapter 12, and of the language 0-2 of Chapter 19. In contrast with these languages,
0-3 handles well both covariant and contravariant Self types, as well as the inheritance
of binary methods, but restricts subsumption. 0-3 is inspired by the class-based lan-
guage PolyTOIL, but includes both class-based and object-based constructs.
We give the type rules of 0-3, including the rules for a new relation called match-
ing, and we show that these rules are sound by a translation into ObO}<:w In particular,
we interpret the matching relation via higher-order subtyping, as first discussed in
Chapter 3. The translation gives a concrete example of how Obeu<:11 can model both
class-based and object-based languages at once, with general Self types.
21.1 Features
Many features of 0-3 should be familiar from Chapters 12 and 19, for example object
types, class types, and single inheritance. The main new feature is a matching relation,
written <# [37] .
Basically, an object type A == Object(X)[ ... ] matches another object type B ==
Object(X)[ ... ](written A <# B) when all the object components of B are literally present
in A, including any occurrence of the common variable X. So, for example,
Object(X)[l:X-...?X, m:X] matches Object(X)[l:X-...?X). The rules for matching are thus
different from the rules for subtyping between recursive object types; in fact
Object(X)[l:X-...?X, m:X) <: Object(X)[l:X-...?X) does not hold.
Matching is the basis for inheritance in 0-3, much as subtyping is the basis for
inheritance in 0-1 and 0-2. That is, if A <# B, then a method of a class for B may be
inherited as a method of a class for A . In particular, binary methods can be inherited.
For example, a method 1of a class for Object(X)[l:X-...?X] can be inherited as a method
of a class for Object(X)[l:X-...?X, m:X].
An important difference between subtyping and matching is that matching does
not support subsumption: when a has type A and A <# B, it is not sound in general to
infer that a has type B. With the loss of subsumption, it is often necessary to parameter-
ize over all types that match a given type. For example, a function with type
(Object(X)[l:X-...?Xj)-...?C cannot be applied to an object of type Object(X)[l:X-...?X, m:X].
For flexibility, one should try to rewrite it with type AlI(Y<#Object(X)[l:X-...?Xj)Y-...?C,
enabling the application to an object of type Object(X)[l:X-...?X, m:X]. Unfortunately,
this parameterization may be cumbersome, and requires some foresight.
The matching relation is defined only between object types, and between variables
bounded by object types. No subtype relation appears in the syntax of 0-3, although
subtyping is still used in its type rules. We will have that if A and B are object types and
A <: B, then A <# B. Moreover, if all occurrences of Self in B are covariant and A <# B,
then A <: B.
21.2 Syntax
The syntax of 0-3 is given below. The most salient syntactic difference with respect to
0-2 is that the variable binders are restricted by matching, not by subtyping.
Syntax of 0-3 types
A,B::= types
X type variable
Top maximum type
Object(X)[I;WB; ;E1..n] object type
Class(A) class type
All(X<#A) B match-bound quantified type
The type Class(A) is intended as the type of classes that generate objects of type A,
just as in 0-1 and 0-2. However, the interpretation of these types is different here:
Object(X)[ ... ] is understood simply as Il(X)[ ... ] (as in 0-1), whereas Class(A) is inter-
preted in a more sophisticated way (to be discussed later), using higher-order opera-
tors. As in 0-1 (but not 0-2) there is no variance restriction on the occurrences of the
Self variable in an object type. The bounded universal quantifiers of 0-2 are replaced
21. A LANGUAGE WITH MATCHING 307
by different bounded universal quantifiers, reflecting the switch from the subtype rela-
tion to the matching relation.
The object construct provides a way of creating objects directly, without first
defining a class; this can be useful because methods in a direct object construction need
not be as parametric as in a class definition. A method update construct provides a way
of replacing object attributes with new attributes; unlike in 0-2, there is no parametric-
ity requirement on the new attributes. The method update construct is intended with
a functional semantics: we do not consider imperative features in Part III. Therefore,
and for simplicity, we do not include a field update construct.
The construct new generates an object from a class. The base class is root. In the
subclass construct, the header c:C with(x:X<#A) identifies a superclass c, its class type
C (having, say, n components), and the type A (having n+m components) of the objects
generated by the subclass. The binder introduces a self variable x, and a Self type vari-
able X that matches A. (The binder x:X<#A stands for x:X and X<#A.) Both the object
type A and the class type C must be known; that is, they must not be type variables.
As in 0-2, the methods of a superclass c are implicitly inherited into a subclass
except when they are replaced by methods in the override list. In addition, the methods
in the with list are added to the subclass. The effect of super is obtained via class selec-
tion: the construct c"I(A,a) activates a method I of a class c, provided that appropriate
object types and arguments are supplied; the criterion for appropriateness will be
given in terms of matching.
We use our standard convention that a missing variance annotation indicates
invariance. In examples, we use basic types and function types. As usual we can
encode the function type A~B as Object(X)[arg-:A, val+:B), for X fresh. In addition, we
use some abbreviations:
Root ~
Class(Object(X)(J)
class with(x:X<#A) li=b i i€l .. n end ~
subclass of root:Root with(x:X<#A) h=b i iE1..n override end
subclass of c:C with(x:X<#A) .. . super.l ... end ~
subclass of c:C with(x:X<#A) ... c"I(X,x) ... end
object(x:A) . .. 1copied from c ... end £
object(x:A) ... l=c"I(A,x) .. . end
a.I:= b ~ where xtFV(b) and a: A,
a.l := method(x:A) bend with A clear from context
21.3 Examples
As a demonstration of the features of 0-3, we consider three examples: binary trees,
cells, and points. We discuss the examples informally; the formal details can be filled
in using the type rules of Section 21.4.
308 PART III. HIGHER-ORDER CALCULI
21.3.2 Cells
As a second example, we recode storage cells. In this example, the proper methods are
indicated with variance annotations +; the contents and backup attributes are fields.
Cell ~
Object(X)[contents: Nat, get+: Nat, set+: Nat-7XI
cel/Class : Class(Cell) ~
class with (self: X<#Cell)
contents = 0,
get = selJ.contents,
set = fun(n: Nat) selJ.contents := n end
end
ReCel/ ~
Object(X)[contents: Nat, get+: Nat, set+: Nat-7X, backup: Nat, restore+: Xl
reCel/Class : Class(ReCell) ~
subclass of cel/Class:Class(Cell)
with (self: X<#ReCell)
backup = 0,
restore = selJ.contents := selJ.backup
override
set = fun(n: Nat) cel/Class"set(X, selJ.backup := selJ.contents)(n) end
end
We can also write a version of ReCeli that uses method update instead of a backup field:
ReCell' ~
Object(X)[contents: Nat, get+: Nat, set+: Nat-7X, restore: Xl
reCel/Class' : Class(ReCell') ~
subclass of cellClass:Class(Cell)
with(self: X<#ReCell')
restore = selfcontents := 0
override
set = fun(n: Nat)
let m = selJ.contents
in cellClass"set(X,
selJ.restore := method(y: X) y.contents := mend)
(n)
end
end
end
310 PART III. HIGHER-ORDER CALCULI
We obtain ReCell <: Cell and ReCell' <: Cell, because of the covariance of X and the
positive variance annotations on the method types of Cell where X occurs. On the other
hand, we have also ReCell <# Cell and ReCell' <# Cell, and this does not depend on the
variance annotations. In 0-2, the subtypings ReCell <: Cell and ReCell' <: Cell do not
depend on the variance annotations.
A generic doubling function for all types that match Cell can be written as follows:
double: AIl(X<#Cell) X~X ~
fun(X<#Cell) fun(x: X) x.set(2*x.get) end end
21.3.3 Points
As a final example, in order to allow comparisons with 0-1 and 0-2, we recode the
points of Sections 12.3 and 19.3.1. In 0-3, we define:
Point ~ Object(X)[x: Int, eq+: X~Bool, mv+: Int~Xl
In contrast with the corresponding programs in 0-1 and 0-2, no uses of typecase
are required in this code. The use of typecase is not needed for accessing the color of a
point after moving it. (Typecase is needed in 0-1 but not in 0-2.) Specifically, the over-
riding code for mv does not need a typecase on the result of super.mv(dx) in the defini-
tion of cPointCiass. Other code that moves color points does not need a typecase either:
cPoint : CPoint £ new cPointCiass
movedColor: Color £ cPoint.mv(I).c
Moreover, 0-3 allows us to specialize the binary method eq as we have done in the def-
inition of cPointClass (unlike 0-2). This specialization does not require dynamic typing:
we can write super.eq(other) without first doing a typecase on other.
Thus the treatment of points in 0-3 circumvents the previous needs for dynamic
typing. The price for this is the loss of the subtyping CPoint <: Point, and hence the loss
of subsumption between CPoint and Point.
21.4 Typing
The type rules of 0-3 are based on two inclusion judgments: subtyping between types
and matching between object types. Correspondingly, there are two well-formedness
judgments: being a type and being an object type. Every object type is of course a type.
Judgments
Ero environment E is well-formed
ErA A is a well-formed type in E
ErA:: Obj A is a well-formed object type in E
ErA<: B A is a subtype of B in E
E r vA <: v'B A is a subtype of B in E, with variance annotations v and v'
ErA<# B A matches B in E
Era :A a has type A in E
The judgments for types and object types are connected by the (Type Obj) rule. The
other rules are straightforward, except for the fact that variables bound by matching
count as object types.
312 PART III. HIGHER-ORDER CALCULI
Types
(TypeObj) (Type X) (Type Top)
E I- A :: Obj E', X<:A, E" I- <> EI-<>
EI-A E', X<:A, E" I- X EI-Top
Object types
(ObjX) (Obj Object) (Ii distinct, lJiElo,-:})
E', X<#A, E" I- <> E, X<:Top I- Bj 'ltie1..n
E', X<#A, E" I- X :: Obj E I- Object(X)[I,'\Ji:Bj iE1..n] :: Obj
It follows from these rules that a Self type can never occur as a quantifier bound. This
is not possible because in the rule (Obj Object) the type Bi is formed under the assump-
tion X<:Top, where X is the Self type. Object types and class types may still contain
quantifiers, as long as their bounds are not a Self type.
There are two inclusion rules for object types. The rule (Sub Object) is essentially a
combination of standard rules for subtyping recursive types and object types; the rule
(Match Object) is more liberal, and corresponds to higher-order subtyping of opera-
tors. Note that the subtype relation is critically used in the definition of matching of two
object types, even though matching is the only relation visible in the syntax of 0 - 3. The
matching relation is axiomatized as a reflexive and transitive relation; this is a novelty
with respect to early treatments of matching based on F-bounded quantification [35].
Subtyping
(Sub Refl) (Sub Trans) (Sub X) (Sub Top)
EI-A E I- A <: BEl- B < : C E', X<:A, E" I- <> EI-A
E I- A <: A E I- A < : C E', X<:A, E" I- X <: A E I- A <: Top
(Sub Object) (where A", Object(X)[I,vi:Bi iE l..n+m), A' '" Object(Y)[I,'\Ji':Bi' iEI../I))
E I- A E I- A ' E, Y<:Top, X<:YI- ViBj <: vi' Bi' 'It ie1..n
E I- A <: A'
(Sub Alld)
E I- A' <# A E, X<#A' I- B <: B'
E I- All(X<#A)B <: All(X<#A ')B '
21. A LANGUAGE WITH MATCHING 313
Matching
(Match Refl) (Match Trans) (Match X)
E I- A:: Obj E I- A <# BEl- B <# C E', X<#A, E" I- 0
We now come to the typing rules for terms. They include a subsumption rule:
although subtyping is not generally expected to hold between object types, the sub-
sumption rule is still handy when subtyping does hold. The rules (Val Select), (Val
Update), and (Val Class Select) are all structural rules with respect to matching. This is
necessary because the methods that form classes and subclasses must typecheck with
respect to an unknown type (the Self type) that matches a given object type.
Terms
(Val Subsumption) (Val x)
E I- a : A E I- A < : B E ',x :A, E" I- 0
(Val New)
E I-- c : Class(A)
E I--newc:A
(Val Root)
EI--o
E I-- root: Class(Object(X)[])
The rule for building subclasses, (Val Subclass), requires new methods to be para-
metric in Self (through the assumption E, X<#A, x:X I-- bi : B;). The rule for class selec-
tion, (Val Class Select), exploits this parametricity.
The assumption E I-- Class(A) in (Val Subclass) is used only to simplify the transla-
tion of Section 21.5. The assumption E I-- A <# A' is particularly important; it compares
the object type associated with the subclass against the object type associated with the
superclass. The common attributes of A and A' may differ only in the overridden posi-
tions, whose indices are in Ovr. The subtyping rules for variance come into play here.
For invariant attributes (often fields) the attribute type in A must be the same as the cor-
responding attribute type in A': there is no type specialization on fields. Note, though,
that a field may have type Self. For covariant attributes (often proper methods) the
overriding attribute type can be a subtype of the overridden attribute type. Thus we
can have method specialization on override.
As in 0-1 and 0-2, not all methods of a class for A' can be inherited in a class for
A. Method Ii is inheritable if E, X<# A I-- B/ <: Bi where B/ and Bi are the result types in
A' and A, respectively. This condition holds when Bi' and Bi are identical, for example
when they are both equal to the Self type variable X (hence methods that return self are
21. A LANGUAGE WITH MATCHING 315
inheritable), and when they are both equal to a binary method type X---tC (hence binary
methods are inheritable). In this respect 0-3 is more liberal than both 0-1 and 0-2.
21.5 Translation
In Chapter 20 we saw by example how general Self types could be represented in the
higher-order calculus Ob_: Il. In this chapter, the language 0-3 provides a more
appealing syntax for those examples. Now we show that 0-3 can be translated into
Ob_:1l'
Although the translation is fairly complex, it produces intelligible output. For
example, the result of translating the binary-tree example from 0-3 into Ob_:1l yields
terms similar to the original ObW<:1l terms of Section 20.4.1.
161- Class(Object(X)[I:X])
This type is mapped to:
161- [new+: Li, 1+: 'V(X-<:4)X*--tX*]
which specifies a type for new and a type for the pre-method I. The type X of attribute
I is translated as X within the object type .1, but as X* within the pre-method type
'V(X-<:!1)X*--tX*. As in this example, pre-methods will always have types of the form
'V(X-<:4)X*--tl!; method types will be translated differently in class types and in object
types.
X 3! X
21.5.3 Translation
As the tables of the previous section indicate, the treatment of type variables depends
on the environment in which the type variables are bound. The tables show this depen-
dence informally; in this section we give a formal environment-dependent translation
of types and terms. The translation of a valid judgment E I- 3 of 0-3 is a judgment of
Db"",:)! of the form E. I- ~ where ~ is a function of both 3 and E. A soundness theorem
establishes that if E I- 3 is derivable in 0-3, then its translation is defined and derivable
in Db"",:)!.
We define the translation of 0-3 through several functions:
Translations for 0-3
«E» translates an environment E such that E I- 0 in 0-3
to an environment of Db"",:)!
translates a type A such that E I- A :: Obj in 0-3
to an operator of Db"",:,.
translates a type A such that E I- A in 0-3
to a t ype of Db"",:,.
translates a term a such that E I- a : A in 0-3 for some A
to a term of Dbw<:,.
translates an environment suffix E' such that E, E' I- 0 in 0-3
to an environment suffix of Dbw<:,.
«t}» translates a valid judgment of 0-3 to a judgment of Db"",:,.
318 PART III. HIGHER-ORDER CALCULI
The translations for types, terms, environments, and judgments are as follows.
Translation of 0-3 types
i
«E', X<#A, E"I'lX» ~ X
«EI'lObject(X)[liWBi iEl..n)>> ~ A.(X)[I;\Ji:«E, X<:TopHBi» iEl..n)
Because of the dual interpretation of types, some clauses of the definition are more del-
icate and interesting than they might appear at first. In particular, the X of
Object(X)[ ... ] is treated as a type, whereas the X of Class(Objecl(X)[ ... J) is treated as
an operator.
Translation of 0-3 environments
«16» ~ 16
«E, x:A» ~ «E», X:«EHA»
«E, X<:A» ~ «E», X<:«EHA»
«E, X<#A» ! «E», X<#«EI'lA»
«EHJ6» ! 16
«EHE', x:A» ! «EHE'», x:«E, E'HA»
«EHE', X<:A» ! «EHE'», X<:«E, E'HA»
«EHE', X<#A» ! «EHE'», X<#«E, E'I'lA»
«E I- A» ~ «E» I- «EHA»
«E I- A :: Obj» ~ «E» I- «EOIA» :: Op
«E I- A <: B» ~ «E» I- «EHA» <: «EHB»
«E I-v A <: v' B» ~ «E» I-v «EHA» <: v' «EHB»
«E I- A <# B» ~ «E» I- «EOIA» -<: «EOIB»
«E I- a : A» ~ «E» I- «EHa» : «EHA»
X<#A f- Bi» for iEl..n are derivable in Obox:~ these judgments can be rewritten as
«E», X-<:«E~A» f- «E, X<#A~Bi», for iEl..n. Therefore «E» f- [new+:«E~A»*,
It:V(X-<:«E~A»)X*~«E, X<#AHB i» iE!..n] is derivable; by Lemma 21.5-2 this is the
translation of t).
Cases (Type All<#), (Obj X), (Obj Object), (Sub Refl), (Sub Trans)
Easy.
Case (Sub X)
We have t) == E', X<:A, E" f- X <: A, obtained from E', X<:A, E" f- o. By induction
hypothesis, «E', X<:A, E" f- 0» is derivable in Obox:~ this judgment is «E'»,
X<:«E'HA», «E', X<:AHE"» f- 0 by Lemma 21.5-1. By (Sub X) we obtain «E'»,
X< :«E'HA», «E', X<:AHE"» f- X <: «E' .... A». By Lemma 21.5-3, this judgment is «E'»,
X< :«E' .... A», «E', X<:AHE "» f- X <: «E', X<:A, E"HA»; this is the translation of t).
Cases (Sub Top), (Sub Object), (Sub All<#), (Sub Invariant), (Sub Covariant),
(Sub Contravariant), (Match Refl), (Match Trans)
Easy.
Case (Match X)
We have t) == E', X<#A, E" f- X <# A, obtained from E', X<#A, E" f- o. By induction
hypotheSiS, «E', X<#A, E" f- 0» is derivable in Obox:~ this judgment is «E'»,
X-<:«E'~A», «E', X<#AHE "» f- 0 by Lemma 21.5-1. By (Match X) we obtain «E'»,
X-<:«E'~A», «E', X<#A .... E"» f- X -<: «E'~A». By Lemma 21.5-3, this judgment is «E'»,
X-<:«E'~A», «E', X<#A .... E"» f- X -<: «E', X<#A, E"~A»; this is the translation of t).
obtain «E», x':«EFtA»(<<E .... A»), x:«E .... A» I- «E, x:A .... b;»{x} : «E .... Bi{A&». By Lemma 20.5-
1(3) and renaming of x' to x, we obtain «E», x:«EFtA»(<<E .... A») I- «E, x:A .... b;»(jold(
«E .... A»,x)& : «E .... Bi(A&».
A few further steps are needed before we can form an object through (Val Object)
in Dbwc::,.. First we note that «EFtA» :; A(X)[I,'Ui:«E, X<:Top .... BdX}» iE1..nj; Lemma
21.5-4(1) yields [I,W«E, X<:Top .... BilX}» iE1..n]{X~«E .... A»&:; [liW«E .... Bi{AJ» iE1..nj, and
hence «E» I- «EFtA»(<<E .... A») - [liw«E .... Bi{A&» iE1..nj through (Con Eval Beta). Now
(Val Object) yields «E» I- [li=~(x:«EFtA»(«E""A»))<<E, x:A .... b;»(jold(<<E .... A»,x)) iE1..nj :
«EFtA»(<<E .... A»). It follows that «E» I- !old(«E .... A»,[li=~(x :«EFtA»(<<E .... A»))<<E, x:A .... b;»
(jold(<<E .... A»,x)} iE1..nJ) : «E .... A»; this is the translation of ".
Case (Val Select)
We have" :; E I- a.lj : Bj{A'1, obtained from E I- a : A' and E I- A' <#
Object(X)[I,1Ji:Bi(X} iE1..nj. By induction hypothesis, «E» I- «E ....a»: «E .... A'» and «E» I-
«EFtA'» -<: A(X)[liW«E, X<:Top .... Bi» iE1..nj are derivable in Dbwc::", and by Lemma
21.5-2 we have «E .... A'»:; «EFtA'»*:; J.1(X)«EFtA'»(X). We obtain «E» I- unfold(<<E ....a»)
: «EFtA'»(<<E .... A'») and «E» I- «EFtA'»(<<E .... A'») <: (A(X)[liW«E, X<:Top .... Bi» iE1..nJ)
(<<E .... A'»), hence «E» I- «EFtA'»(<<E .... A'») <: [I,W«E, X<:Top .... Bi»{X~«E .... A'») iE1..nj
through (Con Eval Beta) and «E» I- un!old(<<E ....a»): [ltui:«E, X<:Top .... Bi»{X~«E .... A'»B
iE1..nj by subsumption. By (Val Select) and Lemma 21.5-4(1), we obtain «E» I-
un!old(<<E ....a»).lj: «E .... Bj(X~A'&»; this is the translation of ".
Case (Val Method Update)
We have":; E I- a.lj:= method(x:A')b end: A', obtained from E I- a: A', E I- A' <#
Object(X)[liwBdX} i.l..nj, and E, x:A' I- b : BjiA'}. By induction hypothesis, «E»
I-«E ....a» : «E .... A'» and «E» I- «EFtA'» -<: A(X)[I,W«E, X<:Top .... Bi» iE1..nj are derivable in
Dbwc::v.. By induction hypothesis, also, «E», x:«E .... A'» I- «E, x:A' .... b»{x} : «E,
x:A' .... Bj(A'»> is derivable in Dbwc::,.. We obtain «E», x:«EFtA'»(<<E .... A'») I- «E,
x:A' .... b»{!old(<<EHA'»,x)} : «E, X:A'HBj{A'&» much as in the case of (Val Object) (by
substitution and by Lemma 21.5-2, which says that «EHA'» :; «EFtA'»* :;
J.1(X)«EFtA'»(X)). As in the case of (Val Select), we obtain «E» I- un!old(<<EHa») :
«EFtA'»(<<EHA'») and «E» I- «EFtA'»(<<EHA'») <: [l{ui:«E, X<:Top .... Bi»«X~«EHA'»&
iE1..nj. From Lemmas 21.5-3(1) and 21.5-4(1), we get that «E, X:A'HBj«X~A ')>> :;
«EHBj(X~A'&» :; «E, X<:TOpHBj»«X~«EHA'»l Hence we obtain «E» I- un!old(
«EHa»).lj~~(x : «EFtA'»(«EHA'»))<<E, x:A'Hb»(jold(<<EHA'»,x)B : «EFtA'»(<<EHA'») by (Val
Update); finally we obtain «E» f- !old(«EHA'»,un!old(<<EHa»).lj~~(x:<<EFtA'»(<<EHA'»))
«E, X:A'Hb»(jold(<<EHA'»,x)B) : «EHA'», which is the translation of " .
Case (Val New)
We have ~:; E I- new c : A, obtained from E I- c: Class(A). By induction hypothesis,
«E» f- «EHC» : [new+:«EHA», .. . j is derivable in Db",<:w Hence we obtain «E» f-
«EHc».new : «EHA»; this is the translation of~ .
2l. A LANGUAGE WITH MATCHING 323
In the course of the book, we formulated several type systems for our calculi and
proved their soundness. We also defined three small but interesting programming lan-
guages 0-1,0-2, and 0-3, and we proved the soundness of their rules by translating
them into calculi. This approach to proving soundness seems manageable and general.
Our investigation of typing rules has illuminated some delicate issues. One of
those issues is the treatment of Self types. We found that a combination of simple object
types, recursive types, and higher-order types can serve to explain Self types. The
resulting rules for Self types are sound and prove useful for sub typing and inheritance.
In particular, these rules permit the inheritance of methods that return self, without
global flow analysis, dynamic typing, or retypechecking.
In our experience, object calculi are a good basis for studying object-oriented lan-
guages and for designing new ones. We used our calculi for guiding the design of 0-1,
0-2, and 0-3, and for explaining and comparing their features. These languages
include both class-based and object-based constructs. The inheritance mechanisms dif-
fer from language to language, and correspond, respectively, to those of the Simula
family, the Eiffel family, and the emerging PolyTOIL/Theta family. 0-1 is simple and
convenient, but requires the use of typecase on a fairly regular basis. 0-2 reduces the
need for typecase by the introduction of Self types. Unfortunately, 0-2 cannot deal
well with binary methods. In contrast, 0-3 can handle binary methods satisfactorily,
without typecase. In order to handle binary methods, 0-3 limits subsumption; one
cannot always view instances of a subclass as instances of its superclass, as in 0-1 and
0-2. The trade-off between subsumption and inheritance of binary methods seems
unavoidable. We do not know of a way of combining the advantages of 0-1,0-2, and
0-3 while eliminating all their limitations. The difficulty in reconciling the conflicting
features of 0-1, 0-2, and 0-3 is indicative of the difficulty of designing sound and flex-
ible object-oriented languages.
In summary, we have found that objects are amenable to a precise, coherent under-
standing. As we have shown, we can cope with the complexity of object-oriented lan-
guages through the prism of object calculi. To temper our optimism, we should note
that it is still hard to reason about objects. Our equational theories, though somewhat
rudimentary, have brought into focus some interesting difficulties.
We do not expect our theory of objects to be definitive. Many variants and exten-
sions are possible, and even desirable. In particular, our calculi do not yet address
issues of concurrency, which seem worthy of study. We believe that our theory is a
good starting point for further investigations, and that the development of object cal-
culi will remain a fruitful approach to understanding object-oriented programming.
ApPENDIX
Rules and Proofs
A FRAGMENTS
dObU~:Ob
(Eq Sub Object) (where A;: [li:Bi iELnl, A';: [li:Bi iE1..n+m))
E, Xi:A I- bi : Bi lfiEl..n E, xj:A' I- bj : Bj IfjEn+ l..n+m
E I- [li=C;(xi:A)b i iEl..n] H [li=C;(xi:A')b i iE1..n+m] : A
330 ApPENDIX. RULES AND PROOFS
(Type Const)
EI-o
EI-K
6.<:
(Sub Arrow)
E I- A' <: A E I- B <: B'
E I- A~B <: A'~B'
6.x
(Env X) (Type X)
E I- 0 X~dom(E) E', X, E"I- 0
E, X I- 0 E', X, E" I- X
(Type Rec)
E, XI- A
E I- Il(X)A
(Val Fold) (where A '" /l(X)B(X}) (Val Unfold) (where A'" J.1{X)B(X})
E I- b : BMB El-a : A
E I- fold(A,b) : A E I- unfold(a) : B«AR
~:V
(Type Exists)
E, X f- B
E f- 3(X)B
(Val Pack)
E f- A E f- blAB : BlAB
E f- pack X=A with b{X}:B{X} : 3(X)B{X}
(Val Open)
E f- c : 3(X)B E f- D E, X, x:B f- d : D
E f- open cas X,x:B in d:D : D
(Val Pack<:)
E f- C <: A E f- biq : Biq
E f- pack X<:A=C with b{X}:B{X} : 3(X<:A)B{X}
(Val Open<:)
E f- c: 3(X<:A)B E f- D E, X<:A, x:B f- d: D
E f- open cas X<:A,x:B in d:D: D
!l.=
(EqSymm) (Eq Trans)
Ef-aHb:A Ef-aHb:A Ef-bHC:A
Ef-bHa:A Ef-aHc:A
(Eqx)
E', x:A, E" f- 0
(Eq Pack)
E f- A E f- blAH H b'iA! : BiAH
E f- pack X=A with bIX} :BIX} H pack X=A with b'IX}:BIX} : 3(X)BIX}
(Eq Open)
E f- c H c' : 3(X)B E f- D E, X, x:B f- d H d' : D
E f- open cas X,x:B in d:D H open c' as X,x:B in d':D : D
A. FRAGMENTS 335
(Eva! Repack<:)
E I- b : 3(X)B{X} E, y:3(X)B{X} I- d{y} : D
E I- open bas X,x:B{XI in d{pack X'=X with x:B(X'»»:D H dlb» : D
(EqPack<:)
E I- C <: A' E I- A' <: A E, X<:A ' I- B'{XI <: B{XI E I- biq H b'{q : B'Hq
E I- pack X<:A=C with b{XI:B{X} H pack X<:A'=C with b'{XI:B'{XI : 3(X<:A)B{X}
(EqOpen<:)
E I- c H c': 3(X<:A)B E I- D E, X<:A, x:B I- d H d' : D
E I- open cas X<:A,x:B in d:D H open c' as X<:A,x:B in d':O: D
(Eva! Unpack<:) (where c '" pack X< :A=C with b{X} :B{X})
E I- c: 3(X<:A)BIXI E I- D E, X<:A, x:B{XII- d{X,x} : D
E I- open c as X<:A,x:B{X} in d{X,x}:D H d«C,b«q» : D
(Eva! Repack<:)
E I- b : 3(X<:A)B{X} E, y:3(X<:A)B{X} I- dly} : D
E I- open b as X<:A,x:B{X} in d(pack X'<:A=X with x:B(X'»»:O H d«bB : D
B SYSTEMS
The fragments forming this calculus have been rearranged, so that the rules are
grouped by judgment.
(Env¢) (Env x)
E I- A x~dom(E)
j1J I- 0 E, x:A I- 0
(EqSymm) (EqTrans)
El-aHb:A El-aHb:A El-bHC:A
El-bHa:A El-aHc : A
(Eq Sub Object) (where A", [li:Bi iEl..n], A' '" [Ii :Bi iE1..n +m])
E, xj:A I- bi : Bi 'v'iEl..n E, xj:A' I- bj : Bj 'v'jEn+ l..n+m
E I- [lj=~(xj:A)bi iel..n] H [lj=~(xi:A')bi jel..n+m] : A
El-a : A jEl..n
E I- a.lj H bj{aJ : Bj
E, x:A I- 0 E, X< :A I- 0
340 ApPENDIX. RULES AND PROOFS
(Sub Rec)
E f- Il(X)A Erll(Y)B E, Y<:Top,X<:Yf-A<:B
E f- Il(X)A <: Il(Y)B
(Val Fold) (where A:; !l<X)B(X}) (Val Unfold) (where A:; I1(X)B(X})
E f- b: BIA» Ef-a :A
E f- fo1d(A,b): A E f- unfold(a) : BfA»
B. SYSTEMS 341
(Val Pack<:)
E f- C <: A E HHCH : BHCH
E f- pack X<:A=C with b{X}:B{X} : 3(X<:A)B{X}
(Val Open<:)
E f- c : 3(X<:A)B E f- 0 E, X<:A, x:B f- d : 0
E f- open cas X<:A,x:B in d:D : 0
(EqFun) (EqAppl)
E, x:A f- b H b': B E f- b H b' : A~B E f- a H a' : A
E f- A(x:A)b H A(x:A)b': A~B E f- b(a) H b'(a') : B
(Eq Fold) (where A", /i(X)B(X)) (Eq Unfold) (where A", /i(X)B(X))
E f- b H b' : BHAD Ef-aHa': A
E f- fo1d(A,b) H fold(A,b ' ): A E f- unfold(a) H unfold(a' ) : BHAR
(EqPack<:)
E f- C <: A' E f- A' <: A E, X<:A' f- B'{X} <: B{X} E f- bHCH H b'HCH : B'(C}
E f- pack X<:A=C with b{X):B{X) H pack X<:A'=C with b'{X) :B'{X) : 3(X<:A)B{X)
342 ApPENDIX. RULES AND PROOFS
(EqOpen<:)
E I- C H c' : 3(X<:A)B E I- D E, X<:A, x:B I- d H d' : D
E I- open cas X<:A,x:B in d:D H open c' as X<:A,x:B in d':D : D
(Eva! Repack<:)
E I- b : 3(X<:A)B{XI E, y:3(X<:A)BIXII- d{y} : D
E I- open bas X<:A,x:BIXI in d(pack X'<:A=X with x:BCX')J:D H deb) : D
E, x:A I- 0 E, X<:A I- 0
(Val Wrap) (where A'" C;(X)B{X)) (Val Use) (where A", 9X)B{X})
E f- C <: A E f- biC} : B[e} E f- c: A E f- D E, Y<:A, y:B{Y} f- d: D
E f- wrap(Y<:A=C)b{Yj : A E f- use cas Y<:A, y:BlY} in d:D : D
(Eq Sub Object) (where A'" [/i:Bi i.l..n], A' '" [/i:Bi i.l..n+",])
E, Xi:A f- bi : Bi 'v'iEl..n E, Xj:A' f- bj : Bj 'v'jEn+ l..n+m
E f- [li=c,;(Xi:A)bi ie!..n] H [li=c,;(Xi:A')bi ie!..n+m] : A
(Eval Select) (where A;: [/;:Bi i<I.."), a;: [li=<;(xi:A')b;{xi) i<l .. "+m))
E I- a : A jE l..n
E I- a.lj H bj{a) : Bj
(Eva! Update) (where A;: [li:Bi i<I.."), a;: [li=<;(xi:A')bi i< l .. "+m))
E I- a: A E, x:A I- b : Bj jEl..n
E I- a.ljfr.r;,(x:A)b H [lj=r;,(x:A')b, li=r;,(xi:A')b; i E(l..n +mHj'] : A
o
The following table defines the sets of free variables of parts of environments:
Scoping for (incomplete) environments
FV(~) ~ II
FV(E, x:A) ~ FV(E) v (FV(A) - dom(E»
FV(E, X<:A) ~ FV(E) v (FV(A) - dom(E»
Case (Env x)
In this case E'{X,X»;: (El{X,Xl z:D{X,X») and we have E, X<:C, El{X,XI I- D{X,XI.
By induction hypothesis (2), E, Y<:C, Z<:C, El{Y,ZII- D{Y,ZI . Hence by (Env x), E,
Y<:C, Z<:C, EdY,ZI, z:D{Y,ZII- o.
Case (Env X<:)
If E';: f/l, then we have E I- 0 and E I- C. By (Env X<:), E, Y<:C I- o. By weakening,
E, Y<:C I- C. By (Env X<:) again, E, Y<:C, Z<:C I- o.
If E'{X,X} ;: (El{X,X}, W<:D(X,X}), then we have E, X<:C, El{X,X} I- D{X,Xl By
induction hypothesis (2), E, Y<:C, Z<:C, EdY,ZII- D{Y,ZI. By (Env X<:) E, Y<:C,
Z<:C, El{Y,ZI, W<:D{Y,ZII- o.
(2)
Case (Type X<:)
If D{X,X} ;: X, then either D{Y,ZI ;: Yor D{Y,ZI ;: Z (since X~FV(D)). We have E,
X<:C, E'{X,X} I- o. By induction hypothesis (1), E, Y<:C, Z<:C, E'{Y,ZII- 0, and by
(Type X<:) both E, Y<:C, Z<:C, E'{Y,ZII- Yand E, Y<:C, Z<:C, E'{Y,ZII- Z.
If D{X,X& ;: W;;!! X, then Wedom(E, E') and W F Y, W;;!! Z, so D{Y,ZI ;: W. By induc-
tion hypothesis (1), E, Y<:C, Z<:C, E'{Y,ZII- 0, and by (Type X<:) E, Y<:C, Z<:C,
E'{Y,ZII- W.
Case (Type Top)
In this case, DIX,X} ;: Top;: D{Y,ZI . We have E, X<:C, E'{X,XII- o. By induction
hypothesis (1), E, Y<:C, Z<:C, E'{Y,ZII- 0, and by (Type Top) E, Y<:C, Z<:C, E'{Y,ZI
I- Top.
Case (Type Object)
In this case, D(X,X};: [li:D/ if1..") . Hence D{Y,ZI ;: [li:Di{Y,ZI if1..") for some Di such
that 0/ ;: Di(X,X}, for iel..n. We have E, X<:C, E'{X,X} I- Di{X,XD. By induction
hypothesis (2), E, Y<:C, Z<:C, E'{Y,ZII- D;{Y,ZI. By (Type Object), E, Y<:C, Z<:C,
E'{Y,ZII- [li:D;{Y,ZI if1..").
Case (Type Arrow)
In this case, D{X,XJ;: D'~D". Hence D{Y,ZI;: DdY,ZI~D2{Y,ZI for some 0 1 and
02 such that 0';: DlrX,X} and 0";: D2iX,XI. We have E, X<:C, E'{X,XB I- DI{X,X»
and E, X<:C, E'(X,X} I- D 2{X,X». By induction hypothesis (2), E, Y<:C, Z<:C, E'{Y,ZI
I- DdY,ZI and E, Y<:C, Z<:C, E'{Y,ZII- D 2{Y,ZI. By (Type Arrow), E, Y<:C, Z<:C,
E'{Y,ZII- DdY,ZHD2{Y,ZI.
Case (Type Rec<:)
In this case, DiX,X} ;: Jl(W)D' with W~dom(E)u{X, Y,ZI. Hence D{Y,ZI ;:
Jl(W)(DdY,Z}) for some 0 1 such that 0' = DI{X,Xl We have E, X<:C, E'(X,Xt
C. PROOFS 349
hypothesis, E, X<:C, Y<:X, E'{Y,XB I-- A'liY,XD <: A'liX,YH; that is, E, X<:C, Y<:X,
E'(Y,XB I-- Al(Y,X, <: AlCX,YB. Also by induction hypothesis, E, X<:C, Y<:X,
E'iY,XB, Z<:A'dY,X} I-- A2(Y,XB <: A2(X,YJ, that is E, X<:C, Y<:X, E'(Y,X},
Z<:AliY,X} I-- A2IY,X) <: A2(X,YB. By (Sub Alk:), E, X<:C, Y< :X, E'iY,X} I--
'v'(Z<:Al(X, n)A2{Y,X} <: 'v'(Z<:A l {Y,XB)A2(X, YB. That is, E, X<:C, Y<:X, E'iY,XB I--
('v'(Z<:AI)A2){Y,XB <: ('v'(Z<:A I )A2)(X, YB.
Case A{V'",lr) ;: 3(Z<:At{V'",lI"})A2{V'",lI"}
We have E, X<:C, E'iX,XJ I-- Al{X,XH and E, X<:C, E'{X,X}, Z<:Al(X,XH I-- A2«X,Xl
By induction hypothesis, E, X<:C, Y<:X, E'iY,XJ I-- AliY,X} <: A}iX, YB and E, X<:C,
Y<:X, E'(Y,X}, Z<:AliY,XB I-- A2IY,XH <: A2iX,YB. By (Sub Exists<:), E, X<:C, Y<:X,
E'(Y,X} I-- 3(Z<:AliY,Xi)A2(Y,XB <: 3(Z<:A l iX,YB)A2(X,YB. That is, E, X<:C, Y<:X,
E'{Y,XB I-- (3(Z<:AI)A2)(Y,XB <: (3(Z<:AI)A2)(X, YB.
o
We are now ready to prove the two lemmas stated in Section 13.3.
Lemma C.1-6 (Lemma 13.3-1)
Assume E'(Y+,X-} and B{X+}.
If E, X<:A, E'{X,X} I-- B(X}, then E, X<:A, E'(X,AJ I-- B(X} <: BfAB.
Proof
The assumption E, X<:A, E'(X,XB I-- B(X} gives E, X<:A, Y<:X, E'iY,XJ I-- B(Y, <:
B(X} (for a suitable Y) by Lemma Cl-5, and then E, Y<:A, E'(Y,AB I-- Bin <: BiAB
by Lemma Cl-2. We conclude by renaming.
o
Lemma C.1-7 (Lemma 13.3-2)
Assume B(Y+,X-}.
If E, X<:A I-- BeX,X} and E I-- C} <: C2 and E I-- C2 <: A, then E I-- B(C},C2B<: BiC:!,C1l
Proof
The assumption E, X<:A I-- BiX,XB gives E, X<:A, Y<:X I-- BKY,X» <: B«X,n (for a
suitable Y) by Lemma Cl-5, then E, Y<:C2 1-- B(Y,C2B<: B(C:!, YB by Lemma Cl-2,
and E I-- B(C 1,C2' <: B(C:!,ClB by Lemma Cl-2 again.
o
Proof
The proof is by induction on the size of AIV",U-}, and is similar to the proof of
Lemma C.1-5. We detail only the case for objects.
Case A{V",U-} == Obj(Z)[ljtJj:Bj jE1..n]
Without loss of generality, we assume that U and V are different from Z.
If tJj == 0, then U, V~FV(Bi)' We have E, X<:C, E'{X,X»' Z<:Top f-- B;{Z+}, and E, X<:C,
E'{X,Xi, Z<:A{X,XH f-- B;{Z+} by Lemma 16.4-1. By induction hypothesis E, X<:C,
Y<:X, E'«Y,X), Z<:A{Y,XH f-- B;{Z} <: BiIZ}. Therefore E, X< :C, Y<:X, E'{Y,X»,
Z<:A{Y,XH f-- 0 BiIZ} <: 0 BiIZ }.
If tJi ==
+, then B;{Z+}{V",U-}. We have E, X<:C, E'{X,X»' Z<:Top f-- BiIZ}{X,Xl and E,
X<:C, E'«X,Xl Z<:A{X,X} f-- BiIZliX,X} by Lemma 16.4-1. By induction hypothesis
E, X<:C, Y<:X, E'{Y,Xl Z<:A«Y,X} f-- B;{ZHY,X} <: B;{ZliX,y»' Therefore E, X<:C,
Y<:X, E'{Y,X}, Z<:A{Y,X} f-- + B;{ZliY,XH <: + B;{Z}(X, y».
If \Ji == -, then BiIZ+}{U+, V'J. We have E, X<:C, E'{X,X}, Z<:Top f-- Bi{Z}(X,X», and E,
X<:C, E'{X,X}, Z<:A«X,X} f-- Bi{Z)(X,XH by Lemma 16.4-1. To apply the induction
hypothesis we need to consider B;' ~ B;{Z}(V,U), so Bi'{Z+}{V",U-} and B;{Z}{D',D"»
== B;'{ZHD',D"} for any D' and D". We have E, X<:C, E'«X,X}, Z<:A«X,X} f--
B;'{ZHX,Xl By induction hypothesis, E, X<:C, Y<:X, E'{Y,X», Z<:AiY,XH f--
B;'{ZI{Y,XH <: B;'IZ!iX,Yi; that is, E, X<:C, Y<:X, E'{Y,X), Z<:A{Y,X» f-- B;{Z}(Y,X»
<: B;lZHX,YB. Therefore E, X<:C, Y<:X, E'«Y, X», Z<:A{Y,XB f-- -Bi{ZHX,YB <:
-B;{ZHY,Xl
Then E, X<:C, Y<:X, E'{Y,XB f-- A«Y,XB <: A{X, YH follows by (Sub Object).
o
Lemma C.2-2 (Lemma 16.4-6)
Assume B{Y+,X-}.
If E, X<:A f-- B{X,XB and E f-- C1 <: C2 and E f-- C2 <: A, then E f-- B«C 1,C2 } <: BK2,C1}.
Proof
As for Lemma C.1-7.
o
«mi=x/ ie', mk=Yk' keK») E ((mi:R~Ti if'». By folding, it follows that (((mi=xi ie'»,
((mi=xi' if', mrYk' keK») E R, and hence (((mi=xi ie'», ((mi=x/ ie!, mk=Yk' keK») E T .
o
Proposition C4-3
If (x,x') E ((mi:Ti ie')1 then either x = x' = .l or x, x' E (L~D).
Proof
Since ((m i:Ti ie')) ~ U (1l(S)F(S) I F E Gen, F ~ A(S)((mi:S~Ti if'»), we prove that if
(x,x ' ) E Il(S)F(S) and F ~ A(S)((mi:S~Ti iEl» then (x,x') E ((.l,.l)} U (L~D) x (L~D).
From this it follows that (A(X)X,A(X)X) E Il(S)F(S)~({(.l,.l)} U (L~D) x (L~D)) for
each F ~ A(S)((mi:S~Ti if'» and, by Proposition 14.2-11, that (A(X)X,A(X)X) E ((m i:Ti
if'))~({(.l,.l)} U (L~D) x (L~D)) .
If F ~ A(S)((mi: S~ Ti ie'», then F(S) has the form ((mj:S~ Tj je l » . If (x,x') E Il(S)F(S),
»,
unfolding yields (x,x') E ((mj: (Il(S)F(S))~Tjjel)). By definition of (( ... we have (x,x ' )
E {(.l,.l)} U (L~D) x (L~D) .
o
Proposition C4-4
If (x,x ' ) E ((mi:Ti ie')1 then x(mk), x'(mk) E (D~D) and (x(mk)(x),x '(mk)(x')) E Tk for all
kEI.
Proof
Again Proposition 14.2-11 applies. It suffices to prove that if (x,x ' ) E Il(S)F(S), F ~
A(S)((mi: S~Ti ie'», and k E I, then (x(mk),x'(mk)) E (D~D) x (D~D) and (x(mk)(x),
x'(mk)(x')) E h
If F ~ A(S)((mi:S~Ti ie'», then F(S) has the form ((mk:S~h .. . ». If (x,x') E Il(S)F(S),
».
unfolding yields (x,x') E ((mdll(S)F(S))~h .. . By Proposition C.4-3, either (x,x')
= (.l,.l) or x, x' E (L~D). If (x,x ' ) = (.l,.l), then the result is trivial. So we assume that
x, x' E (L~D). An application yields (x(mk),x'(mk)) E (Il(S)F(S))~Tk, hence (x(mk),
x ' (mk)) E (D~D) x (D~D); a second application yields (x(mk)(x),x ' (mk)(x ' )) E h
o
Proposition C4-5
If (x,x')E ([mi :Ti ie')) and (y,y') E ((mi:Ti ie'))~Tk for some k E I, then (x(mk~Y)'
x'(mk~Y'») E ((mi:Ti if ')).
Proof
Again Proposition 14.2-11 applies. It suffices to prove that if (x,x ' ) E Il(S)F(S), F ~
A(S)((mi: S~Ti if '», k E I, and (y,y') E ((mi:T i if'))~h then (x(mk~y),x '(mk~Y'») E
((m i:Ti ie ' )).
356 ApPENDIX . RULES AND PROOFS
If F ~ A,(S)((mi: S~Ti iEI», then F(S) has the form ((mk:S~h .. . )). If (x,x') E Il(S)F(S),
unfolding yields (x,x') E ((mk:(Il(S)F(S)hh .. .)). Now (y,y') E ((mi:Ti iEI))~Tk
implies (y,y ' ) E (Il(S)F(S»~h So by Proposition C.4-1 (x(mkf-y),x '(mkf-Y') E ((mi:
(Il(S)F(S)hTi iEI». By folding, (x(mkf-y),x'(mkf-Y') E Il(S)F(S)!;; ((mi:Ti iEI)).
o
Next we state a substitution lemma.
Lemma C.4-6 (Substitution)
(1) HBJm(x<-(cJ,,) = HB{qJm
(2) Hb{X}Dp = Hb(qlp
(3) Hb{xlnp(x<-(clp) = Hb(cJDp
o
Finally, we check the equational rules one by one.
Lemma C.4-7
The equational rules are sound.
Proof
We treat most of the rules, omitting only the trivial (Eq x), (Eq Symm), and (Eq
Trans). We treat even the basic rules from the A,-calculus, because our induction is
superficially different to those of Amadio and Cardone, for example.
(Eq Subsumption)
E I- a H a': A E I- A <: B
E I-a Ha' : B
This rule is immediately sound, since subtyping is simply inclusion.
(EqTop)
El-a : A El-b:B
EI-a H b : Top
From the hypotheses we know that HaDpand HbDp' are not *. Hence (HaDp,Hblp') E HropJm.
(EqFun)
E, x:A I- b H b' : B
E I- A,(x:A)b H A,(x:A)b' : A~B
Consider TJ and (p,p') consistent with E, and (v,v') E [AJm. Let 9 = p(Xf-v) and 9' =
p'(Xf-V'). We have that (HA,(x:A)bDp)v = (A,(u)HbBp(x~u»(v), and hence [A,(x:A)bMv) =
HbBe. Similarly, HA,(x:A)b'Dp'(v') = WDw. Since TJ and (9,9') are consistent with E, x:A,
the hypothesis yields ([bBe,[b'De') E [BJm, so ([A,(x:A)bDp(v),HA,(x:A)b'Dp'(v'» E [BJm, and
([A,(x:A)bDp,[A,(x:A)b'Dp') E [AJm~[BJm . That is, ([A,(x:A)bDp,[A,(x:A)b'Dp') E [A~BJm .
C. PROOFS 357
(EqAppl)
E f- b H b' : A~B E f- a H a': A
E f- b(a) H b'(a ' ) : B
The first hypothesis yields ([b]p,[b ']p') E [A~BJh" so ([bDp,[b ']p') E [Allr,~[Bllr, . Hence
[b]w [b'Dp' E (D~D). We obtain ([aBp,[a 'Bp' ) E [Allr, from the second hypothesis. Hence
([b]p([aBp),[b'Bp·([a'Bp.) E [Bllr" with [b(a)Dp = [b]p([aDp) and [b'(a ')Bp' = [b'8p-([a 'Bp' )'
Finally, we derive ([b(a)Bp,[b'(a')]p') E [Bllr,.
(EqFun2<:)
E, X<:A f- b H b': B
E f- A(X<:A)b H A(X<:A)b' : 'd(X<:A)B
Consider 11 and (p,p') consistent with E, and R E CUPER such that R ~ [Allr, . Let 'I')
= 11(Xf-R). We have that [A(X<:A)b]p = [b]p' Similarly, [A(X<:A)b'Bp' = [b']p" Since 'I')
and (p,p' ) are consistent with E, X<:A, the hypothesis applies, yielding ([b]w[b'Dp' )
E [B]", hence ([A(X<:A)bDp,[A(X<:A)b']p') E [BB". Since this is true independently of
the choice of R, ([A(X<:A)b]w[A(X<:A)b'Dp') E ['d(X<:A)Bllr,.
(Eq AppI2<:)
E f- b H b': 'd(X<:A)B{X) E f- C <: A
E f- b(C) H b'(C) : Blc}
Since [CR'l ~ [Allr" we have that ['d(X< :A)Bllr, ~ [Bllr,(x<--Ictr,), while [Bllr,(X<-iC),,) = [BKillr,
by Lemma C.4-6. Moreover, [bDp = [be C)]p and [b ']p' = [b'( C)Dp" The hypothesis yields
([b]p,[b'Bp') E ['d(X<:A)Bllr" and hence ([b(C)Dr,[b '(C)Bp') E [BKHllr,.
(EqPack<:)
E f- C <: A ' E f- A' <: A E, X<:A' f- B'{X) <: B{X)
E f- biO H b'KH : BlO
E f- pack X< :A=C with b{X):B{X) H
pack X<:A '=C with b' {X) :B' {X) : 3(X< :A)B{X)
This case is similar to that for (Eq AppI2<:), noting that [B'llr,(x<-(ctr,) ~ [3(X<:A)Bllr"
[b{c}Dp = [pack X<:A=C with b{X):B]w and [b '{O]p' = [pack X<:A'=C with b'{X):B'Dp"
(EqOpen<:)
E f- c H c' : 3(X<:A)B E f- D E, X<:A, x:B f- d H d': D
E f- open cas X<:A,x:B in d:D H open c' as X<:A,x:B in d':D : D
By the first hypothesis, we have that ([cDp,[c'Dp') E URECUPER. RQAfi>J [Bllr,(X<-R), and we
wish to derive that ([dDp(x<--lcDp),[d'Bp'(x<--lclp'» E [Dllr,. By Proposition 14.2-11, it suffices
358 ApPENDIX. RULES AND PROOFS
to prove that if (v,v') E [B1,(X.....R) for some R !:;;; [A1, then ([d]p(x<-V),[d'Dp,(x .....v) E [D1,.
If TJ and (p,p') are consistent with E, then TJ(Xf-R} and (p(Xf-v},p' (Xf-V')) are consis-
tent with E, X<:A, x:B. The third hypothesis yields ([d~p(x.....v),[d']p,(x<-V ) E [D1,(X<-R),
and since X cannot occur in D (because E I- D) we obtain ([d]p(x<-V),[d'Ip,(x .....v) E [D1,.
(Eq Sub Object) (where A == [lj:Bj i<l..n), A' == [lj:Bj i<I ..n+",))
E, Xi:A I- bi: Bi 'v'iELn E, Xj:A' I- bj : Bj 'v'jEn+ Ln+m
E I- [li=~(xi:A)bi iE1..n] H [li=~(xi:A')bi iE1..n+m] : A
(Eq Select)
E I- a H a' : [li:Bi iE1.."] jE Ln
E I- a.lj H a'.Ij : Bj
One hypothesis yields ([a]p,[a']p') E [[lj :Bi i<1.."]1" that is, ([a]p,[a']p') E ((Ii:[B j ]Jq i<1..")].
The second one yields ((A.(x:A)bDwIA(X:A)b')p' ) E [[lj :B j iE1.."]~Cj]Jq, that is, ([A.(x:A)b]w
C PROOFS 359
P,,(x:A)b']p')E ((li:[Bi]M iEl .. n])~[Bj]M. (The argument is a special case of the one for (Eq
Fun).) By Proposition C4-3, [alp = [a']p' = .1. or [alp, [a']p' E (L~D). If [alp = [a'[p' = .1.,
then immediately [a.lj;.<;(x:A)b]p = [a'.lj;.<;(x:A)b']p' = .1., so ([a.lj~<;(x:A)b]w[a'.lj;'
<;(x:A)b']p) E [[li:Bi iE1.. nl]M. Otherwise, [a]p, [a']p' E (L~D). Proposition C4-5 yields
([alp(ljf-[A(x:A)b]p),[a']p'(lj~[A(x:A)b']p'» E ([li:[Bi]M iE1..n]). Since [a]p, [a']p' E (L~D),
this last result means ([a.lj;.<;(x:A)b]w[a'.lj;.<;(x:A)b']p') E [[li:Bi iE1..nl]M.
(Eval Beta)
E I- A(x:A)b{x} : A~B E I- a : A
E I- (A(x:A)b{x})(a) H b{aB : B
Consider TJ and (p,p') consistent with E. As in the case of application, the hypoth-
eses yield ([(A(x:A)b)(a)]p,[(A(X:A)b)(a)]p') E [B]M. Because of the form of A(x:A)b, we
have that [A(x:A)b]p' E (D~D) and hence [(A(x:A)b)(a)]p' = [b]p'(xHaDp') ' Lemma C4-6
yields [(A(x :A)b{x})(a)]p' = [b{aB]p" Therefore ([(A(x:A)b{x})(a)]p,[b{aB]p') E [B]M.
(Eval Eta)
E I- b : A~B xtdom(E)
E I- A(x:A)b(x) H b: A~B
Let (v,v') E [A]M. The hypothesis yields that [b[w [b[p' E (D~D) . We obtain that
(Eval Beta2<:)
E I- A(X<:A)b{X) : V(X< :A)B{X) E I- C < : A
E I- (A(X<:A)b)(C) H b{C» : B«q
(Eva! Eta2<:)
E I- b : V(X<:A)B X¢.dom(E)
E I- A(X<:A)b(X) H b: V(X<:A)B
By the first hypothesis, we have that ([cDp,[cDp') E UR ECUPER, RdAItJ [BBn(X....R). We first
prove that (HdDp(x ....lclp),ldDp,(x .....cip,) E [D~. By Proposition 14.2-11, it suffices to prove
that if (v,v') E [B~(X ....R) for some R ~ HA~ then ([dDp(x<-V),[d'Dp,(x ....v) E [DB.,. If 11 and
(p,p') are consistent with E, then 11(Xt-R) and (p(n-v),p' (xt-v'» are consistent with
E, X<:A, x:B. The third hypothesis yields ([dDp(x....v),Hd'Dp,(x ....v) E HD~(X ....R). Since X
cannot occur in D (because E I- D) we have ([dBp(x ....v),[d'Bp,(x....v) E HD~ . Thus we
obtain that ([dBp(n-lclp),[dBp,(x ....lclp.) E [D~. By definition [open (pack X<:A=C with
b{X):B(X)) as X<:A,x:B(X) in d(X,x):DDp = [dDp(x .....clp); in addition, since HbDp' = [cDp"
[d{C,b(Cmp' = HdUp-(x<-nclp·) by Lemma C.4-6. Hence we obtain that (Hopen (pack
X<:A=C with b{X) :B(Xj) as X< :A,x:B{X) in d(X,x):DDp,[d(C,b(Cmp') E [Din.
(Eval Repack<:)
E I- b : 3(X<:A)B{X) E, y:3(X<:A)B{X) I- d(y) : D
E I- open bas X<:A,x:B{X) in d(pack X'< :A=X with x:B«X '&»:D H d{b& : D
We have that [open b as X< :A,x:B{X) in d«pack X'<: A=X with x:B«X'&B :3(X'<:A)
BHX'BBp is [dDp(x ..... blp). The result thus follows from the hypothesis, by Lemma C.4-6.
(Eval Select) (where A;: [li:Bi iEI."), a;: [li=~(xi:A ')bi{xi ) iELn +m))
E I- a: A jEl .. n
E I- a.lj H bj«aB : Bj
The hypothesis yields ([aDp,[aD p' ) E [A~ that is, «(I;=[A(X;:A')Mp»,((/;=HA.(x;:A ')b;Dp'»)
E ((1; :[ B;~ll. The argument for (Eq Select) gives ([a .ljDp,[a.ljDp·) E [BjlhJ . In addition, [aDp'
C. PROOFS 361
Since [fold(A,unfold(a))Bp = A(v )([a Dp(l.», we prove that (A(v )([a Ml.) ),[a Dp') E ITA Ih, from
the hypothesis that ([aD",[alp') E [AIh,. Since [AIh, = Univ-t[BIh,(X<--iAh!)' we prove that
if (v,v') E Univ then «A(v)([aMl.»(v),[allp.(v'» E [BIh,(x<--iA!rJ)' that is, that ([aBp(l.),
[aDp'(v'». This follows immediately from (l.,v') E Univ and ([aDp,[aDp') E [AIh,.
6 Untyped Calculi
Objects and methods .... .. . . .... . .... . . . . . ..... . .. .... . . . . . . . .. ....... 57
Terminology . . . . ... .. .. .. . . .. . . . .... .. . . . . . . ... .. . . . . . . . .. .. ... . .. . .. 58
Primitive semantics .. .. ....... . .. .. .. . . ... . . .. . . ... . . .. .. ..... ... .. . . . 59
Syntax of the r;-calculus . . . . . ... . .. . ... . . ... ... .. .. . ........ . . . . .. ..... 60
Object scoping . .... . .. . . ... .. .. . . .. . .. .. . . . . .. . .. . . . .. .. .. .. . . .. .. . . . 61
Object substitution . .. .. . ..... . ... . .. . .. . . .. . . .. .. ... .. .. ... . .. . .. . . . . 61
Equational theory ... . . . . .... .. ... . ...... . . . . . . . .. . . .... . ... . ... .. ... . 63
Operational semantics .... ... . .. ... .. . . . . . .. . . .. . .... . . . . . . ... ... . ... . 64
Relations between terms . . . . . . .. .... .. . . . . . . . .. . . .. . . . . .. .. ... . ... . ... 66
Translation of the untyped A-calculus . . . .... . . ..... . .. .. .. ... . .... . . . . . . 66
Translation of default parameters ... . ...... . . . .. . . . . . .. ........ . . ...... . 67
Translation of call-by-keyword . . . . . . .. . . ... . . . .. . .. . . ... . . . . . .. . .. . . . . . 68
Self-application semantics ... .. .. . . . . . . .. .. .. . .. . . .. . . ... ... .. . ... .. ... 76
Recursive-record semantics .. . . . .... .. .. . ... . . . . .. . ... .. .. . .... . .. . .. . . 77
Recursive-record semantics (update) . . . .. . . . . . . .. . . . . . .. . . . . ........ . ... 77
7 First-Order Calculi
Syntax fragment for L'.Ob . . . • ... . .... . .. ... .. . .... . .... .. ..... . . ... . .. . 80
L'.Ob .. .. ... . ... . ... .... . • .. . •. • . . .... .. ... . .. .. . . . . . . .. . .. . . . . •• .. • . . 81
L'.x .. . ... .. .. . . . .... .. . . . . . . . . . . . . . . . .. .. .. . . . . .. ... . .. . . . .... . . .. . .. 82
L'.K . . .. • . .. . ... . . ... ... . .. . . . . . . .. . ... . . . .. . . . . . .. . •. . ... .. . . ... . .. . . 82
L'.--+ • . . .. .. . •. . . .. .. . . .. ... . ..... .. . . . . .. . ... . .. ... . ...... . . • . ... . ... 83
Syntax of the FOb} calculus . . .. .. ... ... . . . . . . .. . . . .. . . ... . . . .. . . . ... ... 83
Operational semantics .. .. .. ... . .. .. ... .. . .. . . . . . . .. ... . .. . . ......... . 86
L'.= . ........ . . . .. . . . . . . . . . .. . . . . . .. . • . .. • . . . •. . .. • •.. . • . . . . . ••. . . . • . . 89
L'.=x .... . ... . . . ..... . . . . ... .. .... . ...... . .... .. .. . . . ... . .. . ..... .. . . . 89
L'.=Ob . ... . ... ... . . . . • . . .. . . ..... . • .. . . . . . .. . . .. . • ....... • ... • ... . .. . . 89
L'.=--+ ....... . . . .. . . .. . . .. .. .... ... .. . . .. ... . ... . . ... . . . . . .. . . ...... . . 90
Translation of the first-order A-calculus . . . . .. . .. ... . ........ . . .... . .. . ... 91
366 A THEORY OF OBJECfS
8 Suhtyping
l'l.<: ............................................................... . .. 93
l'l.<:~ . .. ..... . ........................... . . .. . .. . ... ..... .. .... ...... 94
l'l.<:Ob ........ ... .... .. ..... . ........................................ 94
Modified rules for MinOb1 <: ...... .. .. . ............. . .. ... . . ....... . ... 96
l'l.=<: . .. . ..... . . . ........................... ... .... . .. . ...... . ........ 99
l'l.=<:Ob ..... .. . .. .. . . .... . .. ......................................... 99
l'l.Rcd .. . . ... ..... .. .... . .................................. . ......... 106
l'l.=Rcd .. ... ... . . ...... ......... . ........................ .. .......... 106
Rules for elder .. . . .... ..... . . ... ..... . .... ... . .. ....... .... ......... 108
Object types with variance annotations ........ . . .. ...... .. .. .... ... .. .. 110
Subtyping with variance annotations ... . ... . ... ..... . .. ...... .. ........ 110
Typing with variance annotations .. . ... . ............. . ...... .. .... ... . 111
9 Recursion
l'l.x .... . ..... . ... . .. . . .. ........... . ....... . ... . ... ......... .. ...... 114
l'l.J.l ... .. ....... .... .................... . ...... .. . . ........ . ....... . . 114
l'l.=J.l . ........ . ........... ..... .... . ......... . ...................... . 114
Typed translation of the untyped A-calculus . . . . .. ..... .. . ...... .. ...... 115
l'l.<:x ..... . . .. .... . .. ... .. .. ...... ... ... .. .... . .. . .... . ... .... . . .... 116
l'l.<:J.l ............................. .. .. . .......... ... . . ........ . ... . .. 116
l'l.=<:J.l ... .. . ..... .. .. .......... ... . ............ .. .. ... ............... 116
Syntax of the FOb1<:J.l calculus ........ .. ..... .... ... ....... . ... . ... . .. 117
Scoping for the FObl<:J.l calculus . ... . .... . . .. .. . ... . ....... . ...... . ... 118
Restricted rules for rOb1<:J.l . . ....... .. ..... .. .. . ... . . .. ......... . ..... 119
Modified rule for fold for rMinOb1<:J.l . ..... ... .... . .. . . . .. . .. . ......... 119
Operational semantics for fold and unfold . . .. ......... .. . . ... . . .. ..... . . 120
Environment substitution . . ...... ... .... . ......... .. .. . . .. . .. .... ... . 120
Typing rule for typecase ... .. ..... . . .. ........... . . .. . . . ...... ........ 126
Operational semantics for typecase .... ... ................ . . .. . .. . ... . . . 127
12 A First-Order Language
Syntax of 0-1 types .... .. .... . .... .. . . ..... . . .. .. .. . ... . . . . . ... . . . .. . 154
Syntax of 0-1 terms . . ... .... .... .. .. . . . . . ..... .. ... ... ... . ..... . . . . . 155
Judgments . .. . .. .. . . .. ... . . .. .......... .. ... . . . ... .. .... . .. . .. . .... . 159
Environments .. . . . . .. .. .. . . . .... . . . . . ... ..... .. . . . ..... .. . . . .. .... .. 159
Types . . .. .. . .. .... . ...... . ....... .. ......... . ..... .. . .. . .. . ..... .. . 160
Subtyping .. . .. . . .. .. . . . . .. . .. .. . . . ... . . ...... . .. . . .. ... . .. .. . . ..... 160
Terms .. . . . .. . ... . . . . .. . . .... .. .. . . ... ....... . ... .. .... .. ... . . ... . . . 161
Translation of 0-1 types .. . . . . . .. . ... . ... .. . . .. .... . ....... . . .. .... .. 163
Translation of 0-1 environments . ...... . .. . ...... . ... . . .. . .. . . ....... . 163
Preliminary translation of 0-1 terms ... . ... . ..... .... . . .. .. . . .... ... . .. 164
Translation of 0-1 terms .. . ........ . .. . .......... .. . . ... . ... . . .. . . . . . 164
13 Second-Order Calculi
l!.v .... .. .. .. .......... . ... . .. ....... .. . ... . ... . .. . . . ... . .. .... . .. . . 170
l!.=v ... .. .. .. . . ................. . • ... . . . . . . . ... .. . . ....... .. . . . . . ... 170
l!.<:v . ... .. . .... . . .. . . . .. . .. .... .. . .. . ... ... .. .... .... . . .. . .• .. . ..... 171
l!.=<:v ... . .. . .. .. ... . ..... . . . . .. . ..... . .. ... . .. . .. . .... . . . .. .. .. . .. . . 171
l!.<:3 . .. .. . . ... . • . . .... . . . .. . .. .... . .. . . . ... . ... .. .. .... .. .. . .... . ... 173
l!.=<:3 ..... . .. .... . ......... .. . .. . ... . . . . ... .. .. . .. . .. . .. ... . .. ..... . 174
Syntax of the FOb<:!l calculus .. . . . . . . . ... ... .. . .. . . .. . .. . . . ... . . .. .. . . 176
Scoping for the FOb<:!l calculus . ......... . ... . ... .. . .. . . ... . .... . . . ... 176
Variant occurrences ... . . . . .. .. . . . . .. .. ........................ . ... .. . 177
Translation of the first-order A-calculus with subtyping . . .. . ... .. ... . .... 179
The Self quantifier .. .. . . .. . ... .. . . . .. . . ..... . .. . . ... .... . .. . .. . .. . .. . 181
l!.~. . ... .. . . .. . .. .. ..... ... .. . .. . . . ....... . ........ . . . .. .. .. . .... . . . 182
l!.=~ .. .. .. ... .. . . . .. . . .. . .. . .. ... .. . . . .. ... .... .. .... . ... . ...... .. .. 182
Syntax of the C;Ob calculus . .... . . ... . .. . . .. . . . . . .. .. . . .. .. . .. .. ...... 183
Judgments .... ..... .... .. ........ .. ..... .. ... . ......... ............. 225
Environments, types, and subtypes . . .. ... ... ... .... . ........ . .. ..... .. 225
Terms with typing annotations ........... . . .. . ... .. ... ...... ... .. .. ... 226
Syntax of type parameterization ..................................... .. 228
Operational semantics for type parameterization .. .. ........ .. .. . . . . ... . 228
Quantifier rules ..... ..... . .............. ..... ..... . . . .. .... .. .. . .... 228
Variant occurrences for quantifiers ... .. .................... . ....... .. . 229
19 A Second-Order Language
Syntax of 0-2 types . ...... . . ................... .. ... . .. . . ............ 274
Syntax of 0-2 terms .. ............ . .. ... ... ... . ...... .... ...... .. .... 275
Judgments ............ .... ...... .. ...... . . . ..... . .. . ............. .. . 279
LIST OF TABLES 369
20 A Higher-Order Calculus
Syntax of Ob!JK:J.L ... ........ . ... ... ... .. . . ... . . . . . . ... .. ... . .. . ...... 288
Results . ... . . . ....... .. . . . .. .... . . . . ........... . ..... .. ... . . .. . . .. .. 289
Operational semantics .. . . .. .. ..... . .... .. . . . .. ... . . .. . . . ....... . . .. . 289
Judgments for Obw<:J.L . .. .... ... . .. ... . . ... . . .... . .. . . .. .. . .. . .. . . . ... 290
Environment formation . . . . . . ... . . . . ..... . ... . . . .... . . . .. . . .. .... . .. . 291
Kind formation ... . . ... . . . ....... . .. . .. ... ....... . . . . . ... . ... . . .... . 291
Constructor formation ... . .. . . .. . .. .. . . . .. .. .. . . . .. ..... . . ..... ... ... 291
Constructor equivalence ... .. . ... ... . .. . .. .. . . .. . . .. . . . .. . .. . .. . . . . . . . 291
Constructor inclusion .. . . . . .... .... . . .... . . . .......... . ..... . .. . .... . 292
Term typing .. . . . . . .. . . .. . . .... . . . .. .... . . . .... ... . . .. . . .. . . . ...... . 293
Object-oriented binary trees .. . .. . .... ... . .... . . . . . ... . . . . . ...... .... . 294
Object-oriented binary-tree operator ... .. . . . .. ....... . . ... . . ... .. . . . .. . 295
Object-oriented binary-tree class type .. . ... . .. .... . .. . . . . . . .. .... . ..... 296
Object-oriented binary-tree class .. . . .. . .. . . .. . .. . . . .... .. . . . . . . ..... ... 297
Least upper bounds and normal forms . .. . .... . ... .. .......... . .. . ... . . 299
Judgments for the normal system ..... . . . .. . . . . . ....... .. . . . . . . .. . . .. . . 299
Normal constructor inclusion .. . . . .. ... ... .... . . ... ... ...... . . . . . .. ... 300
A Fragments
l10b U l1<:Ob ........................................................ 329
l1:0b u l1=<:Ob ...................................................... 329
330
l1x .......... .. . . . . ...... .... ....... .. . ....... ....... . .... . . . .......
l1K . .. ... ... ... ... .......... .................................... ... . 330
l1..... . ....... . .............................................. .. ... . ... 330
l1<: .. ......... .. .. .. ........... ...... ... .... . ....................... 330
l1<:..... . ..... .. .............................. .. ..................... .. 331
l1x .... .... ............ . ... . .. .. .. ...... . ....... .. ............ .. ....
331
l1<:x ...... ... ..... ........... ...................... .. .............. 331
l11J. . .............. .... .................. .. .................... . ..... 331
331
l1<:1J. ...... . ..... . ............................. . .... . ...... ... .......
l1v ................................................................. 332
l1<:v ............. . ............... .. ..... .•................. . ........ 332
l13 ..................... ..... ........ . .................... .... ..... . 332
l1<:3 ................................................................ 332
l1: ...... ................. ..... ......... ........................ ..... 333
l1=x ................................................................ 333
Il,........... .... .. ......... ............ .......... ...................... 333
l1=<: ........ .. ............. .. ............. .. ........... ....... .... .. 333
l1=1J. (same as l1=<:IJ.) ........................................ . ......... 334
l1=v . ... ... .. . . ....... ...... ......... .. ......... .........•.......... 334
l1_<:V ........... . ................................................... 334
l1=3 .......... . . ................................................ . .. . 334
l1=<:3 .................•.......... • .......... • ........... .. ..... ..... 335
B Systems
Syntax of the Ob1 <: calculus . . ...................... . . . ... .. ......... . 337
Syntax of the F<:IJ. calculus .......................................... .. 339
Syntax of the <;Ob calculus . .. ... ........ .... .......... . . ... .......... 342
C Proofs
Scoping for (incomplete) environments ................................. 347
List of Notations
The main notations in this book are listed in the order in which they are introduced.
Each chapter may reuse notation introduced in previous chapters; this notation is not
always repeated, unless its meaning changes considerably.
2 Class-Based Languages
class generator of objects .. .. .. .. . . . . . .... .. ... .. . 11
var field attribute or variable.............. ... . . .. 11
method method of an object . . . . . . . . . . . . . . . . . 11. . . . . . . .
self object self-reference ......................... 11
new new object from a class . . . . . . . . . . . . . . . . 12. . . . . .
procedure procedure . ..... . .. . .. . ..... . ........ .. ... . . 13
InstanceTypeOf type of the instances of a class ....... .. . . ..... 12
subclass subclass . . .. .... ..... . .. .. .... . .. . .... .. . . . 15
super reference to a superclass during override ....... 15
override used to replace a class attribute in a subclass ... 15
<: subtype relation .... . . . ............. .... .... 18
typecase discriminating on a type ..................... 19
AxB product type . ... . . ......................... 20
(a,b) pair . . . . .. ............... . . . . . . ..... ... . .. . 20
fst first (i.e., left) element of a pair ....... .. . .. . .. . 20
snd second (i.e., right) element of a pair .. . .. . ..... 20
A-?B function type . ..... . . ...... . ...... . ......... 21
A>ll<B mutable product type . . . ....... .. . . .. . ...... 21
Self the type of self ......... .. ....... . .......... 24
4 Object-Based Languages
object object . .. . . . . ..... . .. . .... . ... .. . .. . . ..... . 35
clone imperative shallow copy of an object . ........ . 36
DescendentOJ relation between an object and its clones . ...... 37
embed inclusion of a method from another object .. . ... 40
372 A THEORY OF OBJECTS
6 Untyped Calculi
equal (informal) ....... . ...... , . ... . ... . . . . . 66
syntactically identical ...... . ......... . ...... 59
equal by definition . .............. ....... ... . 59
syntax definition . . . ..... . .......... . . . ..... . 60
cl>i iEl..n sequence <1», ••• , cl>n .. .. ......... . ....... ... . 58
~ self binder . . . . . . . . . . . . . . . . . . . . . . 57 ..... . .. . .
a, b, c terms ....... ... ......... . . ... . . . . . . .. . ... . . 60
x,Y,Z term variables . . ..... . ... . .................. 60
bin-cD substitution: c replaces the free x's in term b ... . 59
I label . .. ... . . ..... . . . . .... . ... .. .. . . . .. . . . . 60
[li=~(Xi)bi iEl..nJ object ......... . ..... . ... . ...... . .... . . . ... 60
~(x)b method ................... . ...... .... .... . . 60
a.1 field selection or method invocation. . . . . . . . . . . 60
a . l*~(y)b method update ..... . .. . .. ..... ... .. .. .. .... 60
FV(a) free variables of a ......... . ... .... ... . ...... 61
top-level one-step reduction ............... . . 61
--
b{x}
bid
one-step reduction ......... . . . ..... . .. .. .. .. 61
many-step reduction ... . ........ . .... . ...... 61
x may occur free in b . .. . ........ . ... . ...... . 61
bin-c}, when x is clear from context. .......... 61
[... ,I=b, ... J field ... .. ........ . .. . . . ..... . . ... ....... . . . 58
a./:=b field update ... .. . ............... .. . . .... . .. 58
1-3 judgment . . . . ..... .... . ...... . ........ . . ... 62
I-a Hb equivalence judgment . .. . .. . . .. .. ... . .... .. . 63
v result . ........... . . . . .. . . ...... .. ....... .. . 64
I-a-v weak-reduction judgment . ..... ... . .. . .. .. . . . 64
A(x)b function abstraction .. . .... . .. .. .. .. ... . . . ... 66
b(a) function application ......................... 66
«» syntactic translation . . ................ . . . .... 66
fix fixpoint operator . .. ... .. . . . .. .. .. .. . ..... . . . 68
l1(x)b recursive term . .. . .. ... . . ..... . ............. 69
(/i=ai iE1..n) record .. . . .. . .. .. . . ... ... .. .. . ... .. . . . .. . . . 76
a·1 record field selection ... . . . .... ... . . . . .... ... 76
a./:=b record field update .... . .. . . . ...... . ..... .. . . 76
LiST OF NOTATIONS 373
7 First-Order Calculi
j1S empty typing environment. .. .. . .... . . . . .. . . . 79
Ef-5 judgment . .... . . . ... . . . .. . . . . ..... . .... . ... 79
r .~~ Tn
derivation tree ............. . ... .... . ...... .. 79
Judg ment
8 Subtyping
Top the biggest type . .... . . . . . . . .. .. .. .. . . ..... . . 93
E f- A <: B subtyping judgment . .. .... . ... .. . ... . ... .. .. 93
Obl <: first-order ~-calcu l us with subtyping . .. . . . ... . 94
Fl<: first-order A-calculus with sub typing .. . . ...... 94
FObl <: first-order A~-calculus with subtyping . . . .. .. . . 94
E f-a Ha' : A typed equivalence judgment .. .. . .. . . .. ... . . . 99
Class(A) class type for object type A . . . . . . . . . . . . .100 . ... .
374 A THEORY OF OBJECTS
9 Recursion
B(Xt-q substitution: Creplaces the free X's in type B .. 113
blxt-q substitution: C replaces the free X's in term b .. 113
B{XI X may occur free in type B . . . . . . . . . . . . 113 . .....
b{XI X may occur free in term b . . . . . . .. .. ...... .. 113
Ble) B{Xt-q, when X is clear from context . . . .. . .. 113
bie) b{Xt-CB, when X is clear from context ... . .. . . 113
J.1(X)B recursive type ... ..... . ... . . .... . . . .. .... .. 113
fold(A,a) isomorphism into a recursive type ... . ...... . 113
unfold(a) isomorphism out of a recursive type . . ...... .. 113
ObI" first-order c;-ca1culus with recursion . . . ... . ... 114
first-order A-calculus with recursion ..... . .... 114
F1"
FOb}" first-order Ac;-calculus with recursion . .. ...... 114
Obl <:" ObI" with subtyping ........ . . . . .. . .. .. .... 117
FI<:" F1" with subtyping . . . . .. . .. . ... .. . . .. .. . .. . 117
FObl <:" FObl " with subtyping . .. ............ .. ... .. 117
A+B sum (disjoint union) type . .. . ... .... . . .... . . 122
inlAB left injection into a sum type . .. .. .. ..... . .. . . 122
inTAB right injection into a sum type . .... . . . .. ... .. 122
ifABC discrimination on elements of a sum type . . .. . 122
Unit unit type ..... .... . . . . . . .. ............... . . 122
unit the element of Unit . .. ... . ........ . .. . .. .. .. 122
typecase a I (x:A)dt I d2 typecase construct .. .. ... ... . . .. . . .. . . . .. .. 126
12 A First-Order Language
Top the biggest type .... . . . ... . . . . . . . .. . . . . . . ... 154
Objed(X)[livi:Bi iE1..n) object type . .. ..... . . . ... . . .. ..... ... ... .. . 154
Class(A) class type ..... . . .. . . . ..................... 154
objed(x:A) li=b i iE1..n end object ... . ...... . . . . .. . . .. .. . .. . . . ..... . .. 155
a.l := method(x:A) bend method update . ............. . . . .. . . .. . .... 155
newc new object from a class .. .. . .... . . . . ... .. .. . 155
root root class .. .. . . . . . .. .. . .......... . .. . .. .. . 155
subclass of c:C with(x:A) subclass .... .. . ... .. .. . .... . .. .. . .. . .. . .. . 155
li=b i iEn+l .. n+m
override li=bi iEOvr~1..n
end
c"l(a) class selection .... . . .. ......... . . ........ . . 155
typecasea typecase ................. . . . .. .. . .. .. . . .. . 155
when (x:A)bj else b2 end
fun(x :A) bend function ... . . .. ... . ... .. . . . ... . ........... 156
376 A THEORY OF OBJECfS
13 Second-Order Calculi
'If(X)B universally quantified type ... .. ..... . . . ..... 169
A(X)b type abstraction . .. ... .... . ... . .. .. . . .. . . . . 169
b(A) type application . ... . .. . ...... . ... . .. . ..... 169
'If(X<:A)B bounded universally quantified type ......... 171
A(X<:A)b bounded type abstraction ..... .. ....... . .... 171
3(X<:A)B bounded existentially quantified type . . ..... . 173
(A ',b} element of an existential type (informal) .. .... 173
pack X<:A=A' with b:B element of an existential type ..... . ..... ... . . 173
open cas X<:A,x:B in d:D using an element of an existential type ... ... .. 173
F second-order A-calculus without 3 . . . ..... . .. 170
Ob second-order ~-calculus without 3 ........... . 170
FOb second-order A~-calculus without 3 . .. . . . .. .. . 170
F" F with recursion .. .. .. . . . ... . .... .. ........ 170
Ob" Ob with recursion ... ... .. . ...... . . . .. . .... . 170
FOb" FOb with recursion . .. .... . . .. ............ . 170
F<: F with subtyping . . .. . .... .. .. . .. . ... .. .. . . 172
Ob<: Ob with subtyping .. . . . .. . . . . .. . . .... ... . . . 172
FOb<: FOb with subtyping . . ... . .. ... .. . . .. ... .... 172
F<:" Fwith recursion and subtyping . .. . . .... . .... 172
Ob<:" Ob with recursion and subtyping . . . .. . . ..... 172
FOb<:" FOb with recursion and subtyping ..... . ... .. 172
F second-order A-calculus . . . . .......... ... . . .. 175
Ob second-order ~-calculus .. . .. . . . ...... . ...... 175
FOb second-order A~-calculus . . .... . . . ... . ....... 175
F" F with recursion ... ... . ..... .. ............ . 175
Ob" Ob with recursion ..... . .. . ... . .......... .. 175
FOb" FOb with recursion .. . . ..... . ... .. .. . . . ... . 175
F<: F with subtyping ... .... . .. .... .. . . . . . . .. .. 175
Ob<: Ob with subtyping .... ... . .... . . . . ... . .. ... 175
FOb<: FOb with subtyping . .. . . . ...... .. ... ... . . . . 175
F<:" F with recursion and subtyping . ... .. ... .. . .. 175
Ob<:" Ob with recursion and subtyping ... ...... . .. 175
FOb<:" FOb with recursion and subtyping .... . . . ... . 175
A{X+} X occurs positively in A .... . ... . ....... .. .. . 177
A{X-} X occurs negatively in A ................ . . . . 177
A{XO} X occurs neither positively nor negatively in A. 177
A{X+,r} both A{X+) and A{r} . . ..... . . . .. . .. . .... . .. 178
A~1f3B encoding of variant function types . ......... . 178
Ax33 B encoding of variant product types . . . . .. . .. ... 179
~(X)B Self-quantified type .... . .. ... . . . . . . . .... .. . 180
(C,c) element of Self-quantified type (informal) ... . . 180
(~(X)B{X})(C) unfolding BleB of a Self-quantified type . . . .. . 180
LIST OF NOTATIONS 377
14 A Semantics
{...I set ........ ... ... . .. ....... .. . .. . .. .. .. . .. 185
v set union .. .. ..... .. . .. .... .... ..... . ... . . 188
(1 set intersection . . . .. . . ...... ... . . . . .... . .. . 188
set difference ... . . . . . .. .. . . ... . . .. . . ..... .. 187
D the domain (cpo) of values ........ . . ........ 185
~ partial order on a domain ... . ..... . .. .. . .... 186
.L least element of a domain ...... . . .. . ........ 186
U join . .. . .... . ... .. . .. ..... .. .. .. ..... . .... 186
n meet . .. ..... .... . .. . .. . . .. .. .. .. . ... .. ... 188
* the error value .. .... .......... . .. . . . . ... .. . 185
continuous function space ... ...... . . .. .. ... 185
lifting ..... ... . ............ .. .. . .. .. ... ... 186
the countable set of labels .... . ..... . ... ... .. 185
m label ........... ... .... . .............. . ... 185
Li the finite subset {mo, ... , mil of L. . . .. . .... . ... 186
W error domain, {.L, *1 ......... ... .. . . . . ..... . 186
+ coalesced sum . . ... . ....... . .. ... ..... .. ... 186
E coalesced sum membership .... . ... . ........ 198
X cartesian product .. .. .. . .. ... ..... ...... .. . 187
Di approximant of D ... .......... . ............ 186
e embedding . . . . .. .. . .. .. .. ... .. . .... .. ..... 186
r retraction . ........ ..... .. .. . .. . ........... 186
p projection .... . . . ...... ... ... . . ... . . . .. . . . . 186
function composition . .... ... . ... . .. . ..... . . 186
function restriction . ... ... .. . .. . ... . . .. .... . 186
((ml=Xl, ... , mn=xn)} element of L~D ... . . ....... ... . . .. . ...... . 187
j(l~x) function extension .. ....... . ....... . . .. . ... 187
(x,y) pair . . .. .. ... . . . . ' . .. . ....... . . . .... . .. . . . 186
xPy (x,y) E P .. . . ...... ........ . . .............. 187
(Xi) sequence ..... .. . . .... .. ........ .. .... . .... 186
CUPER complete uniform pers . .... . ... . .. . . . . ... .. 187
Univ the largest cuper . . .... ...... . . . . . . . . .. .. . . . 187
C(P) least cuper containing P ... .. .. . .. .. .. . . ... . 188
distance(R, T) distance between two cupers ... . .. .. . . .. .... 188
I1(S)F(S) unique fixpoint . . . ... . . . ... ... . . . . . .... .... 188
Gen the generators on cupers . .... ... ... ... . ..... 189
~ extension relation on generators .... .. . . ..... 189
378 A THEORY OF OBJECTS
19 A Second-Order Language
AIl(X<:A)B bounded universal type . .. . ... ... . .. . .. . . .. . 274
object(x:X=A) li=bi iEl .. n end object . . . . . . . . .. .. ... . . .. .. .. . . ... . . .... . . 275
a.l := method(x:X<:A) b end method update .. .. . . .. .. .. . . ... . . .. ... . . .. 275
subclass of c:C with(x:X<:A) subclass . .. . . .. .. .. . .. .... . . . ... . . .. . .. .. . 275
li=bi ien+l..n+m
override li=bi iEOvr~l..n end
c"l(A,a) class selection . . . . . . . . . . . . . . . . . . . .275 ... . ... . .
fun(X<:A) bend type abstraction . . . . ..... .. .. ... . ... . . . . . . . 275
b(A) type application . . ... . .... .. ... ... ... . .. . .. 275
20 A Higher-Order Calculus
ObO><:1l higher-order ~-caIculus . . .. . . . .. . . . . ..... .. . 287
Ty the kind of all types . . . . . . . . . . . . . . . . 287 . . .. . .. .
K~L the kind of the operators from kind K to kind L 287
380 A THEORY OF OBJECTS
Act! ..... . ... .. ... . . ... . . . . . . [78, 80] Oaklisp ....... .. ... . .. ... ..... . . [76]
Beta . . . .. .. .. ... . . .. . .. ... . .. ... [86] Oberon .. . .... . . . . . . . . . . ..
. . [107]
. . .
C++ .. . . . .... . .. .... . . . .. . . . . .. [115] Obliq . . ... . .... .. ... . ... .. .. . .. . [41]
Cecil .. . ... . . .... ... . . ... ... .. .. [49] Omega . ...... . .... . . . . .... .. [26, 27]
CLOS . ... . ... . ..... ..... .. . .. . . [96] PolyTOIL . .. . .. . .. . . . . . . . . . . [37] ... .
Eiffel ...... . .. . ....... . .... . .. [88, 89] Rapide . . . .. . . .. ...... . . . . .. .. .. [75]
Ellie ......... .. .. ... . . ... . .. . . . . [21] Sather ... . . . . . .. . .. . . . . . . . . . [116]
. ..
Emerald . .. . . .. .. . ... . . . ... . . . . [105] School . . . . . . . . . . . . . . . . . /109] . . .... ..
Garnet .. . . ........ . . . ... . .. . ... [94] Self .......... . .... ... ... [12, 121, 122]
Java .. . .. .. . . . . . . . . . . . . . ... . . . . . [23] Simula . .. .. .. ... .. . .. ... . .. . [25, 57]
Kevo . ...... .... . .. ..... . .. . ... [117] Smalltalk ..... . .. .... .... . ... .. . [66]
Modula-3 . . . . . . . . . . . . . . .... [95] . . . . Theta
. . .... . . .. . . . .. . . . .... ... .. . [58]
NewtonScript .... . .. . . .. .. . .... . [22] Trellis/Owl . . .. .. ............. . [110]
Bibliography
[1] Abadi, M. 1994. Baby Modula-3 and a theory of objects. Journal of Functional Pro-
gramming 4(2); 249-283.
[2] Abadi, M. and Cardelli, L. 1994. A semantics of object types. In Proceedings of the
Ninth Annual IEEE Symposium on Logic in Computer Science, 332-341.
[3] Abadi, M. and Cardelli, L. 1994. A theory of primitive objects: Second-order systems.
In Proceedings of the European Symposium on Programming. Lecture Notes in
Computer Science 788,1-25. Springer-Verlag.
[4] Abadi, M. and Cardelli, L. 1995. On subtyping and matching. In Proceedings of the
European Conference on Object-Oriented Programming. Lecture Notes in Com-
puter Science 952,145-167. Springer-Verlag.
[5] Abadi, M. and Cardelli, L. 1995. A theory of primitive objects: Second-order systems.
Science of Computer Programming 25(2-3); 81-116.
[6] Abadi, M. and Cardelli, L. 1995. An imperative object calculus. Theory and Practice
of Object Systems 1(3); 151-166.
[7] Abadi, M. and Cardelli, L. 1996. A theory of primitive objects: Untyped and first-
order systems. Information and Computation 125(2); 78-102.
[8] Abadi, M., Cardelli, L., Pierce, B., and Plotkin, G. D. 1991. Dynamic typing in a
statically typed language. ACM Transactions on Programming Languages and
Systems 13(2); 237-268.
[9] Abadi, M., Cardelli, L., and Viswanathan, R. 1996. An interpretation of objects and
object types. In Proceedings of the 23rd Annual ACM Symposium on Principles
of Programming Languages, 396-409.
[10] Abadi, M. and Plotkin, G. 1990. A per model of polymorphism and recursive types. In
Proceedings of the Fifth Annual IEEE Symposium on Logic in Computer Sci-
ence, 355-365.
[11] Adams, N. and Rees, J. 1988. Object-oriented programming in Scheme. In Proceed-
ings of the 1988 ACM Conference on Lisp and Functional Programming, 277-
288.
[12] Agesen, 0., Bak, L., Chambers, c., Chang, B.-W., HOlzle, U., Maloney, J., Smith,
R. B., Ungar, D., and Wolczko, M. 1993. The Self 3.0 programmer's reference man-
ual. Sun Microsystems.
[13] Alagic, S., Sunderraman, R., and Bagai, R. 1994. Declarative object-oriented pro-
gramming: Inheritance, subtyping, and proto typing. In Proceedings of the European
384 A THEORY OF OBjECfS
[47] Cardone, F. 1990. Tipi ricorsivi e inheritance in linguaggi junzionali. Ph.D. Thesis,
Dipartimento di Informatica, Universita di Torino.
[48] Castagna, G. 1995. Covariance and contravariance: Conflict without a cause. ACM
Transactions on Programming Languages and Systems 17(3); 431-447.
[49] Chambers, C. 1993. The Cecil language specification and rationale. Technical Report
93-03-05. University of Washington, Dept. of Computer Science and Engineer-
ing.
[50] Chambers, c., Ungar, D., and Lee, E. 1989. An efficient implementation of Self, a
dynamically-typed object-oriented language based on prototypes. In Proceedings of
the ACM Conference on Object-Oriented Programming Systems, Languages,
and Applications. ACM SIGPLAN Notices 24(10); 49-70.
[51] Compagnoni, A. B. 1995. Higher-order subtyping with intersection types. Ph.D. The-
sis, Cip-Data Koninklijke Bibliotheek, Den Haag, Nijmegen.
[52] Cook, W., Hill, W., and Canning, P. 1990. Inheritance is not subtyping. In Proceed-
ings of the Seventeenth Annual ACM Symposium on Principles of Program-
ming Languages, 125-135.
[53] Cook, W. and Palsberg, J. 1994. A denotational semantics of inheritance and its cor-
rectness. Information and Computation 114(2); 329-350.
[54] Cook, W. R 1989. A denotational semantics of inheritance. PhD. Thesis, Computer
Science Dept., Brown University.
[55] Cook, W. R 1989. A proposal for making Eiffel type-safe. In Proceedings of the
European Conference of Object-Oriented Programming, 57-72.
[56] Curien, P.-L. and Ghelli, G. 1992. Coherence of subsumption, minimum typing and
type-checking in F::; Mathematical Structures in Computer Science 2(1); 55-9l.
[57] Dahl, O. and Nygaard, K. 1966. Simula, an Algol-based simulation language. Com-
munications of the ACM 9(9); 671-678.
[58] Day, M., Gruber, R, Liskov, B., and Myers, A. C. 1995. Subtypes vs. where clauses:
Constraining parametric polymorphism. In Proceedings of the ACM Conference on
Object-Oriented Programming Systems, Languages, and Applications, 156-168.
[59] Dony, c., Malenfant, J., and Cointe, P. 1992. Prototype-based languages: From a new
taxonomy to constructive proposals and their validation. In Proceedings of the ACM
Conference on Object-Oriented Programming Systems, Languages, and Appli-
cations, 201-217.
[60] Eifrig, J., Smith, S., Trifonov, V., and Zwarico, A. 1995. An interpretation of typed
OOP in a language with state. Lisp and Symbolic Computation 8(4); 1-4l.
[61] Fisher, K., Honsell, F., and Mitchell, J. C. 1994. A lambda calculus of objects and
method specialization. Nordic Journal of Computing 1, 3-37.
BIBLIOGRAPHY 387
[62] Girard, J.-Y. 1971. Une extension de I'interpretation de COdellll'analyse, et son appli-
cation II /'elimination des coupures dans /'analyse et la theorie des types . In Proceed-
ings of the Second Scandinavian Logic Symposium, 63-92. North-Holland .
[63] Girard, J.-Y., Lafont, Y., and Taylor, P.1989. Proofs and types. Cambridge Univer-
sity Press.
[64] Gonthier, G., Levy, J.-J., and Mellies, P.-A. 1992. An abstract standardisation theo-
rem. In Proceedings of the Seventh Annual IEEE Symposium on Logic in Com-
puter Science, 72-81.
[65] Gordon, A. and Rees, G. 1996. Bisimilarity for a first-order calculus of objects with
subtyping. In Proceedings of the 23rd Annual ACM Symposium on Principles of
Programming Languages, 386-395.
[66] Goldberg, A. and Robson, D. 1983. Smalltalk-80. The language and its implementa-
tion. Addison-Wesley.
[67] Gunter, C. A. 1992. Semantics of programming languages: Structures and techniques.
MIT Press.
[68] Gunter, C. A. and Mitchell, J. c., eds. 1994. Theoretical aspects ofobject-oriented pro-
gramming. MIT Press.
[69] Harper, R. 1994. A simplified account of polymorphic references. Information Pro-
cessing Letters 51(4); 201-206.
[70] Harper, R. and Pierce, B. 1991. A record calculus based on symmetric concatenation.
In Proceedings of the 18th Annual ACM Symposium on Principles of Program-
ming Languages, 131-142.
[71] Hofmann, M. and Pierce, B. C. 1995. Positive subtyping. In Proceedings of the
22nd Annual ACM Symposium on Principles of Programming Languages, 186-
197.
[72] Hofmann, M. and Pierce, B. C. 1995. A unifying type-theoretic framework for objects.
Journal of Functional Programming 5(4); 593-635.
[73] Kamin, S. N. 1988. Inheritance in Smalltalk-80: A denotational definition . In Pro-
ceedings of the 15th Annual ACM Symposium on Principles of Programming
Languages, 80-87.
[74] Kamin, S. N . and Reddy, U. S. 1994. Two semantic models of object-oriented lan-
guages. In Theoretical Aspects of Object-Oriented Programming, C. A. Gunter and J.
C. Mitchell, eds., 463-495. MIT Press.
[75] Katiyar, D., Luckham, D., and Mitchell, J. C. 1994. Polymorphism and subtyping in
interfaces. ACM SIGPLAN Notices 29(8); 22-34.
[76] Lang, K. J. and Pearlmutter, B. A. 1986. Oaklisp: An object-oriented scheme with
first-class types. In Proceedings of the ACM Conference on Object-Oriented Pro-
gramming Systems, Languages, and Applications, 30-37.
388 A THEORY OF OBJECfS
(77) Leroy, X. 1992. Polymorphic typing of an algorithmic language. Ph.D Thesis, Rap-
port de Recherche 1778. INRIA.
(78) Lieberman, H . 1981. A preview of Act1 . AI Memo 625. MIT.
(79) Lieberman, H. 1986. Using prototypical objects to implement shared behavior in
object-oriented systems. In Proceedings of the ACM Conference on Object-Ori-
ented Programming Systems, Languages, and Applications, 214-223. ACM
Press.
(80) Lieberman, H. 1987. Concurrent object-oriented programming in Act 1. In Object-ori-
ented concurrent programming, A. Yonezawa and M. Tokoro, eds., 9-36. MIT
Press.
(81) Liskov, B. H. and Guttag, J. 1986. Abstraction and specification in program develop-
ment. MIT Press.
(82) MacQueen, D. B., Plotkin, G. D., and Sethi, R 1986. An ideal model for recursive
polymorphic types. Information and Control 71(1 -2); 95-130.
(83) Madsen, o. L., Magnusson, B., and Meller-Pedersen, B. 1990. Strong typing of
object-oriented languages revisited. In Proceedings of the ACM Conference on
Object-Oriented Programming Systems, Languages, and Applications, and of
the European Conference on Object-Oriented Programming, 141-160.
(84) Madsen, o. L. and Meller-Pedersen, B. 1988. What object-oriented programming
may be, and what it does not have to be. In Proceedings of the European Conference
on Object-Oriented Programming. Lecture Notes in Computer Science 276,1-20.
Springer-Verlag.
(85) Madsen, o. L. and Meller-Pedersen, B. 1989. Virtual classes, a powerful mechanism
in object-oriented programming. In Proceedings of the ACM Conference on Object-
Oriented Programming Systems, Languages, and Applications, 397-406.
(86) Madsen, o. L., Meller-Pedersen, B., and Nygaard, K. 1993. Object-oriented pro-
gramming in the Beta programming language. Addison-Wesley.
(87) Mellies, P.-A. February 1996. Personal communication.
(88) Meyer, B. 1988. Object-oriented software construction. Prentice Hall.
(89) Meyer, B. 1992. Eiffel: The language. Prentice Hall.
(90) Milner, R 1978. A theory of type polymorphism in programming. Journal of Com-
puter and System Sciences 17(3); 348-375.
(91) Milner, R , Tofte, M., and Harper, R 1989. The definition of Standard ML. MIT
Press.
(92) Mitchell, J. C. 1990. Toward a typed foundation for method specialization and inherit-
ance. In Proceedings of the 17th Annual ACM Symposium on Principles of Pro-
gramming Languages, 109-124.
(93) Mitchell, J.c. and Plotkin, G. D. 1988. Abstract types have existential type. ACM
Transactions on Programming Languages and Systems 10(3); 470-502.
BIBLIOGRAPHY 389
[94] Myers, B. A, Giuse, D. A., and Vander Zanden, B. 1992. Declarative programming
in a prototype-instance system: Object-oriented programming without writing meth-
ods. In Proceedings of the ACM Conference on Object-Oriented Programming
Systems, Languages, and Applications, 184-200.
[95] Nelson, G. ed. 1991. Systems programming with Modula-3 . Prentice Hall.
[96] Paepcke, A ed. 1993. Object-oriented programming: The eLOS perspective. MIT
Press.
[97] Palme, J. 1973. Protected program modules in Simula 67. Modern Datateknik 12, 8.
[98] Palsberg, J. 1995. Efficient inference of object types. Information and Computation
123(2); 198-209.
[99] Palsber& J. and Schwartzbach, M.1. 1994. Static typing for object-oriented program-
ming. Science of Computer Programming 23(1); 19-53.
[100] Palsberg, J. and Schwartzbach, M. I. 1994. Object-oriented type systems. John
Wiley & Sons.
[101] Parnas, D. L. 1972. On the criteria to be used in decomposing systems into modules.
Communications of the ACM 15(12); 1053-1058.
[102] Pierce, B. C. and Steffen, M. Higher-order subtyping. Theoretical Computer Sci-
ence (to appear).
[103] Pierce, B. C. and Turner, D. N. 1994. Simple type-theoretic foundations for object-ori-
ented programming. Journal of Functional Programming 4(2); 207-247.
[104] Plotkin, G. D., Abadi, M., and Cardelli, L. 1994. Sub typing and parametricity. In
Proceedings of the Ninth Annual IEEE Symposium on Logic in Computer Sci-
ence, 310-319.
[105] Rajendra, K. R, Tempero, E., Levy, H. M., Black, A P., Hutchinson, N. c., and
Jul, E. 1991. Emerald: A general-purpose programming language. Software Practice
and Experience 21(1); 91-118.
[106] Reddy, U. S. 1988. Objects as closures: Abstract semantics of object-oriented lan-
guages. In Proceedings of the 1988 ACM Conference on Lisp and Functional Pro-
gramming, 289-297.
[107] Reiser, M. and Wirth, N. 1992. Programming in Oberon: Steps beyond Pascal and
Modula. Addison-Wesley.
[108] Reynolds, J. C. 1983. Types, abstraction, and parametric polymorphism. In Informa-
tion Processing, R E. A Mason, ed., 513-523. North Holland.
[109] Rodriguez, N ., Ierusalimschy, R, and Rangel, J. L. 1993. Types in School . ACM
SIGPLAN Notices 28(8); 81-89.
[110] Schaffert, c., Cooper, T., Bullis, B., Kilian, M., and Wilpolt, C. 1986. An introduc-
tion to Trellis/Owl. In Proceedings of the ACM Conference on Object-Oriented
Programming Systems, Languages, and Applications, 9-16.
390 A THEORY OF OBjECfS
[111] SIS 1976. Data processing - Programming languages - Simula Swedish standard SS
636114. ISBN 91-7162-234-9 Stockholm, Sweden.
[112] Smyth, M. B. and Plotkin, G. D. 1982. The category-theoretic solution of recursive
domain equations. SIAM Journal of Computing 11(4); 761-783.
[113] Snyder, A 1987. Inheritance and the development of encapsulated software systems. In
Research directions in object-oriented programming, 165-188. MIT Press.
[114] Stein, L. A, Lieberman, H., and Ungar, D. 1988. A shared view of sharing: The
treaty of Orlando. In Object-oriented concepts, applications, and databases, W. Kim
and F. Lochowsky, eds., 31-48. Addison-Wesley.
[115] Stroustrup, B. 1991. The C++ programming language. 2nd ed. Addison-Wesley.
[116] Szypersky, c., Omohundro, S., and Murer, S. 1993. Engineering a programming
language: The type and class system of Sather. TR-93-064. ICSI, Berkeley.
[117] Taivalsaari, A 1992. Kevo, a prototype-based object-oriented language based on con-
catenation and module operations. Report LACIR 92-02. University of Victoria.
[118] Taivalsaari, A. 1993. A critical view of inheritance and reusability in object-oriented
programming. JyvaskyHi studies in computer science, economics and statistics 23,
University of Jyvaskyla.
[119] Taivalsaari, A 1993. Object-oriented programming with modes. Journal of Object-
Oriented Programming 6(3); 25-32.
[120] Tofte, M. 1990. Type inferenceforpolymorphic references. Information and Compu-
tation 89(1); 1-34.
[121] Ungar, D., Chambers, c., Chang, B.-W., and H6lzle, U. 1991. Organizing pro-
grams without classes. Lisp and Symbolic Computation 4(3); 223-242~
[122] Ungar, D. and Smith, R. B. 1991. Self: The power of simplicity. Lisp and Symbolic
Computation 4(3); 187-205.
[123] Viswanathan, R. February 1996. Personal communication.
[124] Wadsworth, C. 1980. Some unusual A-calculus numeral systems. In To H. B. Curry:
Essays on combinatory logic, lambda calculus and formalism, J. P. Seldin and J. R.
Hindley, eds., 215-230. Academic Press.
[125] Wand, M. 1987. Complete type inference for simple objects. In Proceedings of the
Second Annual IEEE Symposium on Logic in Computer Science, 37-44.
[126] Wand, M. 1989. Type inference for record concatenation and multiple inheritance. In
Proceedings of the Fourth Annual IEEE Symposium on Logic in Computer Sci-
ence, 92-97.
[127] Wirth, N. 1983. Programming in Modula-2. Springer-Verlag.
[128] Wright, A K. and Felleisen, M. 1994. A syntactic approach to type soundness. Infor-
mation and Computation 115(1); 38-94.
Index
A -name 65
abstract -value 65
- class 11, 101 call-next-method 16
- type 173 class 11
bounded - type - encoding 73, 100, 124, 144, 209,
see partially - type 214,237,295
partially - type 29,173 - in 0-1/2/3 153,274,307
abstraction - selection ISS, 275, 307
bounded type - 171 -typeinO-l/2/3153,274,306
constructor - 288 abstract - 101
operator - 288 concrete - 102
type - 169, 243 leaf - 102
application root - ISS, 276, 307
constructor - 288 class-based I, II, ISS, 273, 305
operator - 288 cloning 36, 130
type - 169,243 interpretation of - 260, 263, 271
assumption (of rule) closed term 61
see premise judgment closure 136, 147, 244
attribute (of object) 12 coalesced sum 186
complete metric space (of cupers) 188
B complete uniform per 187
binary method 31, 208, 294 conclusion (of rule) 62, 79
- in 0-1/2/3 157,274,308 concrete class 102
bounded congruence rule 62
- abstract type constructor 287
see partially abstract type - abstraction 288
- existential quantifier 173 - application 288
- polymorphism 171 context 62
- type abstraction 171 contractiveness 185,188,194,197
- type parameterization 29 contravariance 21, 28, 94, 116, 178, 231
- type variable 171 - annotation 110,223
- universal quantifier 171,228,243, - offunction types 21,94,112,178
288 - of universal quantifiers 171
- universal quantifier in 0-2/3 274, covariance 20,94,116,178,231
306
- annotation 110,223
C - of existential quantifiers 174
call-by- - of object types 109
-keyword 67 - of product types 20, Ill, 179
392 A THEORY OF OBJECTS
inclusion 287 K
inheritance 9, 38 kind 287
-in0-1/2/3153,273,305
- of binary methods 30, 297, 305 L
--is-not-subtyping 30 leaf class 102
dynamic - 46 let 130,245
encoding of - 74, 100,209, 238, 295 lifting 186
encoding of multiple - 75, 101 location 136, 146, 242
explicit - 39
implementation of - 14 M
implicit - 39 match-bound
mixin - 39 - quantified type 306
multiple - 14, 16,44,48 - type abstraction 306
single - 16 - type application 306
single - in 0-1/2/3 153, 273, 305 matching 33, 305
static - 46 method 8,5~ 153,274,307
inner 16 - extraction 14, 93, 106
inspect 20 - invocation 13, 18, 39, 42, 52, 58, 59,
instance interface 102,239 129
interface -lookup 13
- of abstract type 173 - override 9,74
instance - 102 - override in 0-1/2/3 153,273,307
subclass - 102, 239 - specialization 22, 28, 239, 273, 282
interpretation - suite 13
- of objects 257 - type 146, 247
cyclic-record - 262, 267 - update 14, 37, 52, 58, 59, 129, 210,
recursive-record - 78,260,265 224,241
self-application - 76, 185, 259, 264, - update in 0-1/2/3 153,275,307
271 binary - 31, 208, 294
split-method - 263, 268 binary - in 0-1/2/3 157,274,308
state-application - 261,266 fresh - 239
invariance 21,94,116 imperative - update 129, 241
- annotation 110, 223 private - 102
- of object types 94 protected - 102
- of recursive types 116 public - 102
invariant recoup - 212
object shape - 215 virtual- 15
recoup - 214 metric space
structural - 216 complete - (of cupers) 188
structural subtyping - 215 minimum types 95,118
invocation 13, 18, 39, 42, 52, 58, 59, 129 mode-switching 46, 49
multiple
J - dispatch 53
judgment 62, 79 - inheritance 14, 16,44,48
valid - 80 - subtyping 27
394 A THEORY OF OBJECTS