0% found this document useful (0 votes)
1K views

A Theory of Objects by Abdali

Uploaded by

AmitabhaPramanik
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
1K views

A Theory of Objects by Abdali

Uploaded by

AmitabhaPramanik
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 412

Monographs in Computer Science

Editors

David Gries
Fred B. Schneider

Springer Science+Business Media, LLC


Monographs in Computer Science
Abadi and Cardelli, A Theory of Objects

Brzozowski and Seger, Asynchronous Circuits

Selig, Geometrical Methods In Robotics

Nielson [editor), ML with Concurrency

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.

Library 01 Congress Cataloging-in-Publication Data


Abadi, Martin.
A theory 01 objects / Martin Abadi, Luca Cardelli.
p. cm. - (Monographs in computer science)
Includes bibliographical relerences and index.
ISBN 978-1-4612-6445-3 ISBN 978-1-4419-8598-9 (eBook)
DOI 10.1007/978-1-4419-8598-9
1. Object-oriented programming (Computer science) 1. Cardelli,
Luca. II. Title. III. Series.
QA76.64.A22 1996
005.13'I-dc20 96-17038

Printed on acid-Iree paper.

© 1996 Springer Science+Business Media New York


Originally published by Springer-Verlag New York, Inc. in 1996
Softcover reprint 01 the hardcover 1st edition 1996
AII rights reserved. This work may not be translated or copied in whole or in part without the
written permission 01 the publisher (Springer Science+Business Media, LLC), except for
brief excerpts in connection with reviews or scholarly analysis. Use in connectionwith any
form of information storage and retrieval, electronic adaptation, computer software, or by
similar or dissimilar methodology now known or hereafler developed is forbidden.

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.

Production managed by Robert Wexler; manulacturing supervised by Jeffrey Taub.


Camera-ready copy prepared Irom the authors' liles.
Cover illustration, "Object Subject," by Luca Cardelli.

9 8 7 6 5 4 3 2 (Corrected second printing, 1998)

ISBN 978-1-4612-6445-3 SPIN 10669864


Preface
This book develops a theory of objects as a foundation for object-oriented languages
and programming. Our theory provides explanations for object-oriented notions in
terms of a few basic primitives, and can be useful for the design and understanding of
programming languages.
There is a well-established theory of functions, the A-calculus, that serves as a foun-
dation for procedural languages. Our treatment of object-oriented languages largely
parallels existing literature on procedural languages. However, our theory of objects is
self-contained; it is the first that does not require explicit reference to functions or pro-
cedures.
The material in this book is based on research that we carried out during the past
three years; some of this material has appeared in conference and journal articles [2, 4,
5,6,7, 9]. In the development of our ideas, we built on previous work on the foundations
of object-oriented programming as represented, for example, by the collection of
papers edited by Gunter and Mitchell [68] . That body of work produced an understand-
ing of subtyping and several explanations of objects in terms of functions and records.
We discuss some of those previous results in the course of the book.
The chapters of this book are grouped in four main parts, bracketed between a Pro-
logue and an Epilogue. The initial part, the Review, contains a general non-technical
explanation of object-oriented concepts. Part I describes untyped and simply-typed
formalisms for objects and classes. Part II discusses polymorphism, data abstraction,
and the Self type, along with their connections to subtyping and subclassing. Part III
investigates subtyping between type operators, which is more powerful than simple
subtyping and supports more flexible inheritance (for example, for binary methods).
The final sections of the book contain an Appendix summarizing our type rules, a list
of figures, a list of tables, a list of notations, and a list of languages.
The intended audience of the book consists of computer scientists, professionals,
and senior and graduate students with an interest in modern language issues. The
Review is accessible to programmers familiar with at least one object-oriented lan-
guage. It provides a general, concise, and language-independent perspective on object-
oriented concepts, and should make up for lack of broad knowledge about objects. An
acquaintance with the A-calculus and with type systems is helpful background for the
rest of the book. Part I is technical but relatively easy; it does not assume much exper-
tise or skill in programming-language theory. Parts II and III are more challenging.
Subsets of this book are suitable for advanced courses on the theory, design, and
structure of programming languages, even if not entirely devoted to object-oriented
languages. The book can be used to complement standard object-oriented analysis,
vi A THEORY OF OBJECTS

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.

Review 1 Object Orientation


2
••
I••
Class-Based Languages

.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)

Palo Alto, February 1996


Contents
PREFACE ........••..•.•..........• ••••• ...........••...•.••..... v
PROLOGUE • ..•.••.....•........... • •................•........... 1

REVIEW: Object-Oriented Features


1 Object Orientation ... ..................... . .... . .............. 7
1.1 Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . .
1.2 Reuse ...... . .... . ... .... ..... ... ....... . . . . .... . . .... ... ... .... . .. 8
1.3 Classifying Features ........ . .. .. .. . ............. . . . ... . ...... . ..... 9
2 Class-Based Languages ....................................... 11
2.1 Classes and Objects ... . .. . .. .. .. . ........ . . ... . . ...... . .. .. . . . . . ... 11
2.2 Method Lookup ... . . . . .. ... .. ... . . ..... . . ... . . .. ... ... . . . . .... ... . 13
2.3 Subclasses and Inheritance . .. . . . .... .... .... . .... . .. . . ...... .... . . . 15
2.4 Subsumption and Dynamic Dispatch ............... . ....... ... ...... 17
2.5 Type Information, Lost and Found . ... ... . .. ... . ... .... .. . . ....... .. 19
2.6 Covariance, Contravariance, and Invariance . .. ............. .. ........ 20
2.7 Method Specialization .. .. ... .. .. .... . . .. .......... . .. . ..... . . ... .. 22
2.8 Self Type Specialization ... . .... . .... .... .. . . . . . ... . .. .. .. ... ... .. .. 23
3 Advanced Class-Based Features . . . ............... . ............. 25
3.1 Object Types .... .. ... . ........ . . . ..... .. ... .. ... .. .. .. ... . .. .. ... 25
3.2 Distinguishing Subclassing from Sub typing .................. . .... . .. 27
3.3 Type Parameters . . ............ . . . ....... .. ... . ......... . . .. ..... . . 28
3.4 Subclassing without Subtyping .. ............ .. ........ . . . . ... .. . .. . 30
3.5 Object Protocols .. ...... ..... . .. ... ... . ........... ... . . ..... . ... ... 32
4 Object-Based Languages ............ ......... .. . . ..... . ... ... . 35
4.1 Objects without Classes ... ...... . . . .. .. ......... .. . ... . . . . . .. . .... . 35
4.2 Prototypes and Clones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 ... . .... .
4.3 Inheritance by Embedding and by Delegation . . . .. .. ....... .. . ... .. . .. 38
4.4 Embedding . ......... . ... . ................. . ... . . . .... . . .. ... ..... 39
4.5 Delegation .......... . . . . . ............ . ... .. . .. . . . .. . . ... ...... ... 42
4.6 Embedding versus Delegation . . . . ... .... . ... . . ... . .. . . . ... . . .. .... . 45
4.7 Dynamic Inheritance and Mode-Switching .. ..... . . . . . . ... . ... . ...... 46
4.8 Traits: From Prototypes back to Classes? ... ... . . .... . . .. . .... . .. . . . . . 47
4.9 Types for Object-Based Languages . .. ... . ... ... . ........ . .. .. ... . . . . 49
x A THEORY OF OBJECTS

5 Modeling Object-Oriented Languages . . . .... . ...... . .... . . . .. . . 51


5.1 Reduction to Basic Mechanisms . . ... ...... . . .. .. ... . .. ... . . . .. .. .... 51
5.2 The Role of Method Update . ... ... .. . . .. .. . .. . . . . .. . .. . . . . ... .. .. .. 52
5.3 The Scope of this Book . .. .. . .. . . ... .... . .. . . . . . . . .. . ... .. ... .... . .. 53

PART I: Untyped and First-Order Calculi


6 Untyped Calculi . .... .. ... . .. .. .. ... ... . .... ... ... . .. . .. . .... . 57
6.1 Object Primitives . . ....... .. . .. ... ... . .... . . . . . ... . ........ . .... . .. 57
6.2 The <;-Ca\Culus . .. ... . . . .... . ... .. ...... .. ...................... ... 60
6.3 Functions as Objects . ... . . . . . ..... . .... . ... .. . ... . . . . ...... .. ..... . 66
6.4 Fixpoints ... . .... . .... . .... . . . .... .. . . . . .. . ... . ..... . . . . . .. ... .. . . 68
6.5 Examples . . .. . . . ....... . . . .......... . ............. . ......... . .. . . 69
6.6 Traits, Classes, and Inheritance .. . . . ...... . . . .... . .... .. .. . . . . .... .. 73
6.7 Interpretations of Objects ............ .. ............... . ...... . .... . 76
7 First-Order Calculi . . ... . . . . . . . . . . . ... ... . .... . . .... .79
. .. . . ..
7.1 Formal Systems .......... . ....... . ......... . .... . . . .. .. . . ... . ... .. 79
7.2 The Object Fragment . . . . .. .... ... ... .. .... . ..... .. . ... ....... . .... 80
7.3 Standard First-Order Fragments . .. . .. . .. .. .. .... .. . . . . . . . . .. .. . .... 82
7.4 Examples ..... .. .. ....... . .. . ... . ... . .... . ... . .. .. ... . ..... . . .. .. 84
7.5 Some Properties of ObI' . . .. .. . . . . . .. ... ... . ..... . . . . . . ... .. . ..... . 85
7.6 First-Order Equational Theories . . .. .... . ................ . .... . ... .. . 89
7.7 Functions and Fixpoints ..... .. .. . . . ... .. . . ... . ..... . . . ... . ... . . . ... 91
8 Subtyping . . .... . . . .. . .......... . .......... . .............. . .. 93
8.1 Subtyping . .. ... .. .. ....... . . . .. ..... . .. . . .. ........ . ... . .... .. . . . 93
8.2 Examples ... .. .. . ..... . ... .. . ... . . . ..... . . . .. .. . .. . .. ....... .. . . . 95
8.3 Some Properties of Obl <: . . . . . . . . . . . . . . . . . • . . . . • . . . . . . . • . . . . . . . . .. . . 95
8.4 First-Order Equational Theories with Subtyping . .. ...... . ........... . 98
8.5 Classes and Inheritance . ....... . ... .. . . .. .. ... . ..... .. .... . .. . .. .. 100
8.6 Objects versus Records . . . .. ...... . .... ... . . .. .. ... ..... . .. . . .. . .. . 106
8.7 Variance Annotations .. .. .. ..... . ... . .... .. ... ... ... . .... ... . .. ... 109
9 Recursion ...... . .................. . ........ . ............... 113
9.1 Recursion . . . .. . . ... .... . .......... . ... . . .... . .. . ... . ....... ... .. 113
9.2 Recursion and Subsumption ... . ..... . .... . ... .. .. .. .. . ... . . . . . .... 115
9.3 Some Properties of Ob l <:11 . • • • •••• • • .• •• . ••• • • •..• •• .. • .• • . • • • • • . . . 118
9.4 Examples ...... .. . . .. . ........ .. . . ... . .............. . ........... 121
9.5 The Shortcomings of First-Order Typing . .. ... . ...... . . ... . .. .. .. .. . 123
9.6 Towards the Type Self ...... .. ..... .. .. . . . .... . . ... . . ... . .... ... .. 125
9.7 Dynamic Typing . .... .. ... ..... .. . .... . . . . ... . .. . . . ....... . ... . .. 126
CONTENTS xi

10 Untyped Imperative Calculi . .. .. .. . .. ....... . . .. . . . . ... . .... 129


10.1 Syntax . . . . . . .. .. ..... . .. . ................... . . . ... .. ..... .. . . .. 129
10.2 Fields . . .. .. . . . . . . .... . .. . . ... . . . . . . .. .. . . .. . .. .. .. ....... . ..... 130
10.3 Procedures .... . ... .... ... . . . .. . . ..... . . . ... ... . ... . . ...... .. ... 131
10.4 Examples . ...... . . ... . .. . . . . .. .. .. . . . ...... .. . . . . .. . . . .. . . .. ... 133
10.5 Operational Semantics . ... . ... . . .. .. . . .. . . . .. .. . .. .. . . . . . . . . ..... 135
11 First-Order Imperative Calculi . .............................. 141
11.1 Typing . . .. . . . . .. ........... .. ... . . ... .. . ....... . . ... .. . .... . ... 141
11.2 Examples of Typings .... .. . . .. .. .. . . .. .. ......... . .... . ........ . 142
11.3 Classes and Global Behavior Change . .. .. . . . .. .. . . . . .. .. . . . . . ..... 144
11.4 Subject Reduction . ..... . .. ....... .. ... . . . . . . . .. .... . . .. . . . . .. .. . 146
12 A First-Order Language . . . . . . . . . . . . . . . . . . . . . . . . . . .... . 153 . ... . .
12.1 Features .. .. .. . ...... . . .. .. .... ... ... .. ... . .. .. . . .. . .. . .. ..... . 153
12.2 Syntax ... . .. . . . . . . . . . . . . . .. . ..... . . . . . ....... . ....... .. ... . .... 154
12.3 Examples .. . . ... . ..... . .. .. ... .. . ... ... . .. .. . .... .. ... . ....... . 156
12.4 Typing ..... .. . . . ..... . .... ... . . . ..... . .. . .. .. ............ . . . ... 159
12.5 Translation ...... . . . ............ .. .. .. ..... ... . . .. .. . ... . . . . . . . . 162

PART II: Second-Order Calculi


13 Second-Order Calculi. . . . . . . . . ... ... ....... . .169
. . . .. ...... .. .
13.1 The Universal Quantifier . .... . .. . . . ...... . ... . . . . . . ........ . . . ... 169
13.2 The Existential Quantifier . . . . . .. ...... . .. .... . . . . . . .... . . . . . . . . .. 173
13.3 Variance Properties .. . . . . . . ... . ... . ... . . . . . . . .. . .... . .. . .. ... . .. . 177
13.4 Variant Product and Function Types, Encoded in Ob<: ..... ...... .. . . 178
13.5 The Self Quantifier . .. . ...... . . . .. . . . .. .. . ... . . . . . . . . . . . . .. . . . . .. 179
14 A Semantics . ... .. .. .. . .... .. . ........ . . . .... . ............. 185
14.1 The Untyped Universe .. . . .. ... .. . . .. .. .. ......... . .. .... . . . . .. . . 185
14.2 Types in the Untyped Universe . .. . . . . .... . ... . . ... . . .. . . . . . . . . . .. 187
14.3 The Interpretation of Types and Typed Terms ........ . .... . ........ 196
15 Definable Covariant Self Types . ..... . ..... .. ...... .. .. ..... . 201
15.1 ~-Objects .. ... . . ............. . .... . . . . . . . .... . .. . .. . .. . .... .. . . . 201
15.2 Examples, with Typing .. .. . .. . . .... . . ... . ... . . .. . .......... . . ... 205
15.3 Binary Methods and the Covariance Requirement . . . . . . .. .... . . . .. .. 208
15.4 Classes and Inheritance, with Self . .. . ... .. .... .. . . . . . ........ .. . . . 209
15.5 Updating from the Outside ... . . ... ... . . ... .. . . ... . ...... . ... . . ... 210
15.6 Recoup . . . . .. . ........... ... ... ... . . ......... . . . .. . ..... . .. .. . . 212
15.7 Objects with Structural Invariants . . . . . . .. . ... . .. . ... . .. . ......... . 215
xii A THEORY OF OBJECfS

16 Primitive Covariant Self Types . . . . . . . . . .221


. . . . . . . . . . . . . . . . . .. .
16.1 Primitive Self Types and Structural Rules ....... ... ...... . ...... ... 221
16.2 Objects with Structural Rules ....... . . . ... . . ... . . . ... . .... . . . ... . . 222
16.3 Quantifiers . .... .. ... . ...... . ..... . . . .... .. ..... ... . ..... . . . .... 227
16.4 Subject Reduction .. .. ..... . . . . .... .... .... . . . .... .. . ... . . . . . . . . . 229
16.5 Examples . . .... . .. ... ....... . ... . . ... .. .. ... . ... . .. ... . ... . ... . 233
16.6 Classes and Inheritance, with Primitive Self . . . . . .. .. . . . .... ... . .... 237
17 Imperative Calculi with Self Types . .......................... 241
17.1 Syntax of Terms and Operational Semantics . .... . . . ... . .... ... ... . . 241
17.2 Typing with Self . ... ... .. .......... .... .. ... .. .. . ........ . .. . . . . 242
17.3 Quantifiers ..... . . . .. . ...... .. .... .... .. ... .. .. . . .. .. . . . . .. . . . .. 243
17.4 Examples . . . .. . ...... .. .... .. ........ . ........ .. . . . . ..... . . .... 245
17.5 Subject Reduction ... ... . ....... . ... .... . . .. .. . . . .... ... . .. .... . . 247
18 Interpretations of Object Calculi ..... . ....................... 257
18.1 The Interpretation Problem .. . ... . . . ... .. .... .. . .. . ..... ..... ... .. 257
18.2 Untyped Interpretations .. ... . ... . . ..... . ......... .. ........... . . 259
18.3 Typed Interpretations . . ... .. . ..... . . . ......... .... ... ... . . . .... . . 264
19 A Second-Order Language ....... .. ......................... 273
19.1 Features ............. . . .. .... . ..... ....... .... . .. ....... . . ... .. 273
19.2 Syntax .. .. .. . ... .... . .. .. ....... . . . ... . . . . . .. . ........ . ... . ... . 274
19.3 Examples ... . .. . . . . .. ....... . .. .... ... ... . .. .. ...... .. ... . . . .. . 276
19.4 Typing . ... .. .... .... . .. ... . ....... . . . .. .. . ........ . . .. . .. ... ... 279
19.5 Translation .. . . .. ... .. ... .. .. .... . . .... ..... .. . ... .. . . .. . . . ... .. 282

PART III: Higher-Order Calculi


20 A Higher-Order Calculus ...................... . ........... . 287
20.1 Syntax of Obc.><:". .. . .... .. ... . ...... . . . .......... . . ..... . ... ... .. 287
20.2 Operational Semantics . . . ...... . .................. ... . . .. . .. . . ... 289
20.3 Typing ....... . .. ....... . .... . . . . . .. . . . .. . . . ... . .. .. ....... . .... 290
20.4 Binary Methods ............. ..... .. .. .. ... .. . .... . .. .. . . .. ... . . . 294
20.5 Basic Properties of Obc.><:". .. . ... ......• .. ......•............. .. ... 298
20.6 Subject Reduction . .. .. ... ... ..... .. . . . . .. .. .. . .. . .......... . .... 301
21 A Language with Matching . .. ....................... . ....... 305
21.1 Features .. .. . ........ . . . ... .. ... .... . . ....... . . . . .. . .. . . ... . ... 305
21.2 Syntax .. .. . .. . . .. ...... . . . . ....... .... . . . . .. . ...... .. . .. . . .. . . . 306
21.3 Examples .. ... ...... ... . . . . ... . ........ . . . .... . . .. . . . . ....... . . 307
21.4 Typing .... . ........... . . .. . ... ... .. .... . ... . .. .... . ... . .... .. .. 311
21.5 Translation . ..... . .. . .. . . .... . ... .... .. . ... . . .. . . ... .. . ... . .... . 315

EPILOGUE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
CONTENTS xiii

ApPENDIX: Rules and Proofs


A Fragments .. ... ..... .. . . . . . . ... . . .329
. . . . . . . . . . . . . . . . . .. .... . .
Al Simple-Objects Fragments . .. . . .. ... .. .... ... ... ............. . . . . . 329
A2 Other Typing Fragments .... . .... . . . ........ ... .... . ..... ... . . .. . 330
A3 Other Equational Fragments .... . . ... ... .......... .... . . ..... . .. .. 333
B Systems . . .... .. . .. . ... . ................. . .................. 337
B.l The Ob1<: Calculus . . ... . . . .. ... . .... ... .. .... ... . .. .. .. .. . . ... .. 337
B.2 The F<:Jl Calculus . . ..... ... . ........ . . . . . .. . . .. . .. . . .. . . .... . .... 339
B.3 The <;Ob Calculus .... . .. ... . .. ... . ...... .. .... . ... . . .. . .. . ... ... 342
C Proofs .................... . ...... . ......... .. . ......... . ... 347
Cl Proof of the Variance Lemma from Section 13.3 ... .. . ...... ... .... .. 347
C2 Proof of the Variance Lemma from Section 16.4 ...... .. ... .... . . . . .. 351
C3 Deriving the Rules for <;-Objects from Section 15.1.2 . . . .. . ........... 352
C4 Denotational Soundness of Equational Rules .. .. . ....... .. . . .. ..... . 354

LIST OF FIGURES. . . . . . . . .. ... . .. . ..... .. ........ . . .363


. . ...... .

LIST OF TABLES . . . . . . . . . . . . . . . . ... . .. . .... . . . . . . . . . . .365


. . .... .

LIST OF NOTATIONS ........ . .. . .. . ............ . ............ . . . . 371

LIST OF LANGUAGES . ................. . ........ . . . . . .. . . . . .. .• . . 381

BIBLIOGRAPHY . ..... ... ... . ..... .. ................. ... . ....... . 383

INDEX ... . ........ .. . .. ........ . .. .. . . ........ . ...... . ........ 391


Prologue
Procedural languages are generally weIl understood; their constructs are by now stan-
dard, and their formal underpinnings are solid. The fundamental features of these
languages have been distilled into formalisms that prove useful in identifying and
explaining issues of implementation, static analysis, semantics, and verification.
An analogous understanding has not yet emerged for object-oriented languages.
There is no widespread agreement on a collection of basic constructs and on their prop-
erties. Consequently, practical object-oriented languages often support disparate fea-
tures and programming techniques with little concern for orthogonality. This situation
might improve if we had a better understanding of the foundations of object-oriented
languages.
Various calculi of functions (A-calculi) have been used as foundations for proce-
dural languages. It is natural to attempt to use those same calculi for object-oriented
languages. Unfortunately, there always seems to be a mismatch when one tries to
model objects with functions. This mismatch is manageable for untyped languages, but
becomes a serious obstacle when one attempts to explain typed object-oriented lan-
guages in terms of typed function calculi.
In this book, we take a different approach. Instead of taking functions as primitive
and struggling with complex encodings of objects as functions, we take objects as prim-
itive and concentrate on the intrinsic rules that objects should obey. We introduce object
calculi and develop a theory of objects around them. These object calculi are as simple
as function calculi, but represent objects directly.
Our theory of objects clarifies the general principles of object-oriented languages.
Understanding those general principles can be helpful to those studying or designing
programming languages. The theory suggests how to interpret existing constructions,
and how to create and assess new ones. It also provides tools for reasoning about lan-
guages (existing or new), in particular for soundness proofs.
Using object calculi, we explain both the semantics of objects and their typing
rules. We account for a range of object-oriented concepts, such as self, dynamic dis-
patch, classes, inheritance, prototyping, subtyping, covariance and contravariance, and
method specialization. Because of the compactness of object calculi, we hope that they
will serve as a convenient basis for further research on specification, verification, and
analysis of object-oriented programs.
The book begins with an informal review of object-oriented concepts, where we
aim to describe and categorize the fundamental features of object-oriented languages.
The main distinction we make is betwee~ class-based and object-based languages.
Object-based languages simplify and generalize class-based languages by reducing

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
2 A THEORY OF OBjECfS

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.

Preview of Calculi and their Features


The following table summarizes the object calculi that we discuss in this book, and the
main features that they contain explicitly. Although the names of the object calculi are
not meaningful at this point, the table should give an idea of the general coverage of
the various features, and will be helpful later to recall the relationships between the
various calculi. Some calculi have no name (nn). The table does not have rows for the
many features that can be expressed indirectly. A hollow bullet indicates that a feature
can be encoded in a given calculus.

Object calculi and their features


Calculus: c; ObI ObI<: nn ObI!, ObI<:!' nn impc; nn
Defined in: 6.2 7.3 8.1 8.7 9.1 9.2 9.7 10.1 11.1
objects
· · · · · · · · ·
object types
· · · · · · ·
subtyping
· · · · ·
variance
·
recursive types
dynamic types
· · ··
side-effects
· ·
Calculus: Ob Ob!, Ob<: Ob<:!' ~Ob S S\I nn ObO><:1l
13.1- 13.1- 13.1- 13.1- 17.1- 20.1-
Defined in: 13.5 16.2 16.3
13.2 13.2 13.2 13.2 17.3 20.3
objects
object types
· · ·
· · ·
· ·
··· ·· ·· ··· ·· ··
sub typing
· · ·
variance 0 0
· · · ·
recursive types
dynamic types
· · ·
side-effects
·
quantified types
· · · · · · ·
Self types
structural rules
0

· ·· ·· ·· · 0

type operators
·
4 A THEORY OF OBJECTS

We do not provide a systematic treatment of function calculi, because our main


focus is on object calculi. However, in the course of the book we define in detail many
of the common function calculi. The following table summarizes these calculi and their
features.
Function calculi and their features
Calculus: /... Fl Fl<: Fll1 Fl<:11 imp/... F F<: FI1 F<:11
13.1- 13.1- 13.1- 13.1-
Defined in: 6.3 7.3 8.1 9.1 9.2 10.3

. .. · · · . · · ·
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

In this Review we describe many of the standard concepts found in object-oriented


programming, as a preparation for the development of our theory of objects. This first
chapter introduces general notions, including the notion of object. In later chapters we
discuss more concretely how these notions are represented in programming lan-
guages.

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

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
8 REVIEW. OBJEa-ORIENTED FEATURES

a wide range of applications, including the construction of user interfaces, operating


systems, and databases. Although these applications do not concern physical systems,
the object-oriented methodology has carried over.
The properties that confer this broad range of applicability to the object-oriented
approach can be summarized as follows [84, 86).
• The analogy between software models and physical models.
• The resilience of the software models.
• The reusability of the components of the software models.
The first point pertains to analysis. In the object-oriented analysis process, the anal-
ogy with a physical model can be useful in the development of a software model. Much
effort has been invested in systematizing this analysis process; see for example [30J.
The second point pertains to design. Resilience of design in the face of changes is
a consequence of building abstractions. Objects form natural data abstraction bound-
aries and help focus a design on system structure instead of algorithms. Algorithms are
factored into methods that are attached to objects, making the objects behaviorally
autonomous. Because of object abstractions, a design can evolve with fewer pervasive
reorganizations.
The third point pertains to implementation. Objects are naturally organized into
taxonomies during analysis, design, and implementation. This hierarchical organiza-
tion encourages the reuse of methods and data that are located higher in the hierarchy.
Object-oriented programming does not have an exclusive claim to all these good
properties. Systems may be modeled by other paradigms, including ones based on tra-
ditional notions of algorithms and data structures, which were not well developed
when Simula was invented [57J. Resilience can be achieved just as well by organizing
programs around abstract data types [81J, independently of taxonomies; in fact, data
abstraction alone is sometimes taken as the essence of object orientation [19,29) . Reus-
ability can be achieved by modularization [101, 127J and parameterization [90, 91J. Hence
it is possible that, as the availability and awareness of other techniques grow, the
appeal of objects will fade away.
Still, the object-oriented approach has proven uniquely successful. It manages to
integrate good analysis, design, and implementation techniques into a relatively intu-
itive and uniform framework. Moreover, some of its fundamental features are not eas-
ily explained as the union of other well-understood notions; witness the relative
scarcity of formal techniques for analyzing object-oriented programs.

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.

1.3 Classifying Features


In the remaining chapters of this Review we discuss the specific incarnations of object
replacement and method replacement that are found in various kinds of programming
10 REVIEW. OBJECT -ORIENTED FEATURES

languages. The purpose of the Review is to establish basic terminology, and to


describe, informally, the wide and somewhat bewildering spectrum of object-oriented
features that one may want to formalize .
The description and classification of object-oriented notions is not an easy task.
There is a great variety of object-oriented languages. Most languages have overlapping
features, and each feature is present in varying degree in several languages. Therefore
any attempt at categorization has many potential pitfalls. Describing individual lan-
guages leads to discussions about boring, irrelevant, and historically obsolete details.
Comparing languages by their features leads to oversimplification, and is only practi-
cal for a small number of languages. Describing features in the abstract leads to con-
cepts that, in their pure forms, may not exist in any language.
We choose the last option: describing disembodied features. In this way we are
free to focus on the most interesting topics, and we can sidestep complex accidents of
actual languages. To keep the discussion flowing, we relegate to footnotes specific
comparisons with actual languages.
Throughout the Review we use a consistent pseudo-notation for object-oriented
notions. Our pseudo-notation is intended to be suggestive rather than formal. It is not
our goal to detail the semantics and typing rules of either the pseudo-notation or actual
programming languages. We describe the meaning of the pseudo-notation informally,
and we discuss, in general terms, how it relates to features of existing languages. For-
mal counterparts to the pseudo-notation appear throughout the rest of the book.
2 CLASS-BASED LANGUAGES

Class-based languages form the mainstream of object-oriented programming. Among


the best-known class-based languages are Simula, the first object-oriented language,
Smalltalk, the first dynamically typed object-oriented language, and C++, whose main
class-based features are modeled after those of Simula. (Bibliographic references to
these and other languages can be found in the List of Languages at the back of the
book.)
In this chapter we discuss a kernel of properties that, with minimal controversy,
characterize classical class-based languages above and beyond their countless pecu-
liarities. More advanced class-based features are treated in Chapter 3.

2.1 Classes and Objects


Class-based languages are centered around the notion of classes as descriptions of
objects. We begin with an example of a class declaration.
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;
A class is intended to describe the structure of all the objects generated from the
class. The class cell describes storage-cell objects having an integer field named contents,
which is initialized to zero, and two methods named get and set, whose code operates
on the contents field .! The get method has no parameters; when invoked, it fetches the
contents field and returns it. The set method has an integer parameter; it stores the

1 There is a wide spectrum of class notions in programming languages, from classes


detailing values for all fields and code for all methods, to "abstract classes" with as
yet uncoded methods, to classes with no specified values or code at all. We take
classes to have fully specified values and code. The case where there are no values
or code is better covered by object types, described in a later section.

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
12 REVIEW . OBjECf -ORIENTED FEATURES

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

reference attribute record


• contents 0
objoo { ~et (code for get)
set (code for set)

Figure 2-1. Naive storage model.

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;

2.2 Method Lookup


Given a method invocation of the form o.m( ... ), a language-dependent process called
method lookup is responsible for identifying the appropriate method m of the object 0
that has to be executed. Method lookup is not completely trivial because of the stan-
dard storage model used for class-based languages, which is more complex than the
naive model of Figure 2-1. We briefly discuss this storage model, since it is often pre-
sented as the semantics of class-based languages.
Although a class is meant to describe the structure of the objects constructed from
it, methods are not directly embedded into objects as it might appear from the syntax of
classes and from Figure 2-1. Instead, for space efficiency, they are factored into method
suites that are shared by objects of the same class (Figure 2-2). Method lookup must
access these method suites; we say that objects delegate method invocations to the
method suites associated with their classes.

. .. 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

In the presence of inheritance, discussed shortly, method suites may be organized


as a tree, and method lookup may require examining a chain of method suites. With
multiple inheritance, the method suites may form a directed graph (sometimes includ-
ing cycles); method lookup must include a strategy for examining such graphs.
Depending on the language, some method lookups may be performed at compile-time,
based on type information, and some at run-time, based on references to method suites
contained in each object.
Although the description and implementation of method lookup can be quite com-
plex, it is usually designed to produce the illusion that methods are, after all, embed-
ded directly into objects, as in the naive storage model illustrated in Figure 2-1. 4 When
this illusion fails, confusion may result in both language semantics and programming. s
The illusion of embedding for both fields and methods is strengthened by the sim-
ilarity of the standard notations a.x for field extraction and a.m( ... ) for method invoca-
tion. Moreover, in all implementations of method lookup, an occurrence of self within
a method refers to the object that originally received the invocation of that method.
Thus self invariably refers to the object that, according to the embedding interpreta-
tion, appears to contain the method.
It is interesting to notice that some features that would distinguish between
embedding and delegation do not appear in class-based languages. Typical of class-
based languages is the fact that methods, unlike fields, cannot be extracted from objects
as functions, and cannot be updated within objects (by replacing them with other
methods). Method extraction must be prohibited to preserve typing soundness, as we
shall see. Method update is not necessarily unsound. With the embedding implemen-
tation, a method update would affect a single object. With the delegation implementa-
tion, however, it would affect all the objects of a class. 6
Many class-based languages could be implemented equivalently by embedding or
by delegation techniques. For simplicity, we assume the embedding model in the
upcoming explanation of inheritance, and we only occasionally remark on delegation
and method suites. The contrast between embedding and delegation will resurface in
our discussion of object-based languages in Chapter 4.

4 The main class-based feature whose meaning depends on delegation of attributes is


the notion of class variables (fields shared by all objects of a class), as it appears in
Small talk and in Java; similar effects can be achieved with other mechanisms, such
as global variables.
5 CLOS is an example of a language where method lookup is based on a complex
delegation algorithm over a multiple-inheritance class hierarchy [113J. A detailed
understanding of the automatic placement of methods in the hierarchy is necessary
for predicting program behavior.
6 Beta is a class-based language that allows method update in individual objects. It
syntactically distinguishes between methods that are embedded in objects (via
"pattern variables") and methods that are delegated to method suites. In fact, Beta
has several features typical of object-based languages (see Chapter 4); method
update is one of them.
2. CLASS-BASED LANGUAGES 15

2.3 Subclasses and Inheritance


Although the notion of class is rather interesting in its own right, it is a stepping stone
for the notions of subclasses and inheritance. Like any class, a subclass describes the
structure of a set of objects. However, it does so incrementally, by describing exten-
sions and changes to its direct superclass. Fields from a superclass are implicitly repli-
cated in a subclass, and new ones may be added . Methods from a superclass may be
either replicated in a subclass, by default, or explicitly overridden by similarly named
and typed methods? By the subclass relation we mean the partial order induced by the
subclass declarations.
We extend our previous example with the declaration of a subclass reCell (restor-
able cell) of class cell. A restorable cell is a cell with an additional method that restores
the contents to a previous state. The set method is overridden so that it makes a backup
of the contents field before updating it; super.set(n) invokes the old version of set from
the cell class.
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;
Inheritance is the sharing of attributes between a class and its subclasses. This shar-
ing includes the initial values of fields and the code of methods. So the initial value of
the contents field and the code of the get method are inherited by reCell from cell. The
set method could be inherited implicitly like the get method, but is instead overridden
in reCell.
It is common to say, informally, that c' inherits from c, to mean that c' is a subclass
of c. Note, though, that a subclass of a class with no fields may override all the methods
of the superclass and therefore inherit nothing. Conversely, there are mechanisms for
sharing method code that do not rely on the subclass relation (for example, straightfor-
ward procedural abstraction). Hence we are careful to distinguish inheritance from
subclassing.
Without subclasses, an occurrence of self in a class declaration always refers to an
object of that class. With subclasses, this is not the case. In a method that a subclass c'

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

aCell ••--II.~ ~=:J.~=3--I~~


contents I0 ~et I

(code for ~et)
....
set I (code for set)

aReCell. ~ ~

contents I 0 set I (new code for set)


backup I 0 restore I (code for restore)
Figure 2-3. Hierarchical method suites.

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

8 Most object-oriented languages have some notion similar to super as described


here, but details of its meaning vary greatly. We have described a particular ver-
sion, closest to Smalltalk's super (whose meaning is described in terms of method
lookup). See also resend in Self and call-next-method in CLOS. Many class-based
languages do not provide a notion of super relative to the current class. Rather,
they provide ways of extracting methods from a particular class; examples are
selection from object types in Modula-3, qualified messages in C++, and "upward"
qua in Simula. Beta uses a diametrically opposite mechanism, called inner, to allow
virtual methods to invoke methods that override them. Bracha and Cook have pro-
posed a general framework for method combination [33].
2. CLASS-BASED LANGUAGES 17

aCell ••----~~ Icontents• I 0


aReCell. .. . ~et (code for ~et)
contents I 0 set (new code for set)
backup I 0 restore (code for restore)

Figure 2-4. Collapsed method suites.

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

2.4 Subsumption and Dynamic Dispatch


From what we have said so far, it may seem that subclasses are just a convenient mech-
anism to avoid rewriting definitions that already appear in superclasses. Much more
comes into play with the associated notion of subsumption. Consider the definitions:
var myCell: InstanceTypeOj(cell) := new cell;
var myReCell: InstanceTypeOj(reCell) := new reCell;
procedure j(x: InstanceTypeOj(cell) is ... end;
Consider also the following code fragments, in the scope of those definitions:
myCell := myReCell;
j(myReCell);
Here an instance of class reCell is assigned to a variable holding instances of class cell.
Similarly, an instance of class reCell is passed to a procedure f that expects instances of
class cell. Both code fragments would be illegal in a language like Pascal, because the
types InstanceTypeOj(cell) and InstanceTypeOj(reCell) do not match. In object-oriented
languages these code fragments are made legal by the following rule, which embodies
what is often called (subtype) polymorphism:
If c' is a subclass of c, and 0' is an instance of c', then 0' is an instance of c.
or, from the point of view of the typechecker:

9 See, for example, the mechanisms for multiple inheritance adopted in Eiffel, C++,
and CLOS.
18 REVIEW. OBJEcr -ORIENTED FEATURES

(1) If c' is a subclass of c, and 0' : InstanceTypeOfic'), then 0' : InstanceTypeOfic).


We analyze statement (1) further, by introducing a reflexive and transitive subtype
relation «:) between InstanceTypeOf types. This subtype relation is intended, intu-
itively, as set inclusion between sets of values. For now we do not define the subtype
relation precisely, but we assume that it satisfies two properties:
(2) If a : A and A <: B, then a : B.
(3) InstanceTypeOfic') <: InstanceTypeOfic) if and only if c' is a subclass of c.
Together, (2) and (3) yield (1).
Property (2), called subsumption, is the characteristic property of subtype relations.
By subsumption, a value of type A can be viewed as a value of a supertype B. We say
that the value is subsumed from type A to type B.
Property (3), which we may call subclassing-is-subtyping, is the characteristic
property of subclassing in classical class-based languages. Since inheritance is con-
nected with subclassing, we may read (3) as an inheritance-is-subtyping property.
More recent class-based languages adopt a different, inheritance-is-not-subtyping
approach, as is shown later.
With the introduction of subsumption, we have to reexamine the meaning of
method invocation. For example, given the code:
procedure g(x: InstanceTypeOficell) is
x.set(3);
end;
g(myReCell);
we should determine the meaning of x.set(3) during the invocation of g. The declared
type of x is InstanceTypeOficell), while its value is myReCell, which is an instance of
reCell. Because the method set is overridden in reCell, there are two possibilities:
Static dispatch: x.set(3) executes the code of set from class cell
Dynamic dispatch: x.set(3) executes the code of set from class reCell
Static dispatch is based on the compile-time type information available for x. Dynamic
dispatch is based on the run-time value of x; we may say that InstanceTypeOfireCell) is
the true type of x during the execution of g(myReCell), and that the true type determines
the choice of method.
Dynamic dispatch is found in all object-oriented languages, to the point that it can
be regarded as one of their defining properties. Dynamic dispatch is an important com-
ponent of object abstraction: each object knows how to behave autonomously, so the
context does not need to examine the object and decide which operation to apply.lO

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

An interesting consequence of dynamic dispatch is that subsumption should have


no run-time effect on objects. For example, if an application of subsumption from
InstanceTypeOfireCell) to InstanceTypeOficell) were to "coerce" a reCell to a cell by cutting
off its additional attributes (backup and restore), then a dynamically dispatched invoca-
tion of set would fail. The fact that subsumption has no run-time effect is both good for
efficiency and semantically necessary.H

2.5 Type Information, Lost and Found


Although subsumption has no run-time effect, it has the consequence of reducing static
knowledge about the true type of an object. Imagine a root class with no attributes,
such that al1 classes are subclasses of the root class. Then any object can be considered,
by subsumption, as a member of the root class and can be regarded as a useless object
with no attributes.
Less drastical1y, when subsuming an object from InstanceTypeOfireCell) to Instan-
ceTypeOficell), the ability to access the field backup (as wel1 as the method restore) is lost.
This fact, however, does not make the field backup redundant because it is still used,
through self, by the body of the overriding method set. So attributes forgotten by sub-
sumption can still be used thanks to dynamic dispatch.
In a purist view of object-oriented methodology, dynamic dispatch is the only
mechanism for taking advantage of attributes that have been forgotten by subsump-
tion. This position is often taken on abstraction grounds: no knowledge should be
obtainable about objects except by invoking their methods. 12 In the purist approach,
subsumption provides a simple and effective mechanism for hiding private attributes.
If we create a reCell and, by subsumption, give it to a client as a cell, we can be sure that
the client cannot directly affect the backup field.
Most languages, however, provide some way of inspecting the type of an object
and, consequently, of regaining access to its forgotten attributes [83,95). For example, a
procedure with parameter x of type InstanceTypeOficell) could contain the fol1owing
code. The typecase statement binds x to c or to rc depending on the true (run-time) type
of x:
typecase x
when rc: InstanceTypeOfireCell) do .. . rc.restoreO ... ;
when c: InstanceTypeOficell) do ... c.set(3) ... ;
end;

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

Previously inaccessible attributes can now be used in the rc branch. 13


The typecase mechanism is useful, but it is considered impure for several method-
ological reasons (and also for theoretical ones). First, it violates the object abstraction,
revealing information that may be regarded as private. Second, it renders programs
more fragile by introducing a form of dynamic failure when none of the branches
apply. Third, and probably most important, it makes code less extensible: when adding
another subclass of cell one may have to revisit and extend the type case statements in
existing code. In the purist framework, the addition of a new subclass does not require
recoding of existing classes. This is a good property, in particular because the source
code of commercial libraries may not be available.
Although typecase may be ultimately an unavoidable feature, its methodological
drawbacks require that it be used prudently. The desire to reduce the uses of typecase
has shaped much of the type structure of object-oriented languages. In particular, type-
case on self is necessary for emulating objects in conventional languages by records of
procedures; in contrast, the standard typing of methods in object-oriented languages
avoids this need for typecase. More sophisticated typings of methods are aimed at
avoiding type case also on method results and on method arguments (using Self types,
discussed later).

2.6 Covariance, Contravariance, and Invariance


In the rest of this chapter we study some flexible typing techniques for classes and
methods that help in avoiding uses of typecase. To explain these techniques, we first
review the basic subtyping properties of product and function types.
Let us consider product types. The type AxB is the type of pairs with left compo-
nent of type A and right component of type B. The operations fst(c) and snd(c) extract
the left and right components, respectively, of an element c of type AxB.
We say that x is a covariant operator (in both arguments), because AxB varies in the
same sense as A or B:
AxB <: A'xB' provided that A <: A' and B <: B'
We can justify this property as follows.
Argument for the covariance ofAxB
A pair (a,b) with left component a of type A and right component b of type B, has type
AxB.1f A <: A' and B <: B', then by subsumption we have a: A' and b: B', so that (a,b)
has also type A'xB'. Therefore any pair of type AxB has also type A'xB' whenever A
<: A ' and B <: B'. In other words, the inclusion AxB <: A 'xB' between product types is
valid whenever A <: A' and B <: B'.

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'

Argument for the co/contravariance of A~B


If B <: B', then a functionf of type A~B produces results oftype B' by subsumption.
If A' <: A, then f accepts also arguments of type A', since these have type A by sub-
sumption. Therefore every function of type A~B has also type A'~B'whenever A' <:
A and B <: B'. In other words, the inclusion A~B <: A' ~B' between function types is
valid whenever A' <: A and B <: B'.

In the case of functions of multiple arguments, for example of type (AIxA2)~B, we


have contravariance in both Al and A2. This is because product, which is covariant in
both of its arguments, is found in a contravariant context.
Finally, consider pairs whose components can be updated; we temporarily indi-
cate their type by A>!!<B. Given p : A>!!<B, a : A, and b : B, we have operations getLft(p) : A
and getRht(p): B that extract components, and operations setLft(p,a) and setRht(p,b) that
destructively update components.
The operator >!!< does not enjoy any covariance or contravariance properties:
A>!!<B <: A'>!!<B' provided that A = A' and B = B'
We say that >!!< is an invariant operator (in both of its arguments).
Argument for the invariance of A>!!<B
I
If A <: A' and B <: B', can we covariantly allow A>!!<B <: A'>!!<B'? If we adopt this inclu-
sion, then from p : A>!!<B we obtain p: A'>!!<B' and we can perform setLft(p,a') for any a'
: A'. After that, getLft(p) might return an element of type A' that is not an element of
type A. Hence the inclusion A>!!<B <: A'>!!<B' is not sound.
Conversely, if A" <: A and BI/ <: B, can we contravariantly allow A>!!<B <: A I/>!!<B I/? From
p : A>!!<B we now obtain p : AI/>!!<B", and we can incorrectly deduce that getLft(p) : AI/.
Hence the inclusion A>!!<B <: AI/>!!<B" is not sound either.

Much controversy in the object-oriented community revolves around the question


of whether the types of arguments of methods should vary covariantly or contravari-
antly from classes to subclasses. We must stress that the properties of x, ~, and >!!< fol-
low inevitably from our assumptions; we cannot take ~ to be covariant in its left
argument any more than we can take 1t to be 3.0. In the following sections we examine
how variance properties of method types follow inevitably by a similar analysis. We
cannot take method argument types to vary covariantly, unless somehow we change
22 REVIEW. OBJECf-ORIENTED FEATURES

the meaning of covariance, subtyping, or subsumption (e.g., as in [48]). According to


our definitions, covariance of method argument types is statically unsound: if left
unchecked, it may result in unpredictable behavior. 14

2.7 Method Specialization


In our discussion of subclasses we have taken the simplest approach to overriding,
requiring that an overriding method has exactly the same type as the overridden
method. This condition can be relaxed to allow method specialization, that is, to allow an
overriding method to adopt different argument and result types, specialized for the
subclass. We still do not allow overriding and specialization of field types: fields are
updatable, like the components of the type A>!I<B, and therefore their types must be
invariant.
Suppose we use different argument and result types, A ' and B', for an overriding
method m:
class c is
method m(x: A): B is ... end;
method ml(xl: AI): Bl is ... end;
end;
subclass c' of c is
override m(x: A ' ): B' is ... end;
end;
In determining the admissible A' and B' we are constrained by the possibility of sub-
sumption between InstanceTypeOJ(c') and InstanceTypeOJ(c). When 0' of type Instance-
TypeOJ(c') is subsumed into InstanceTypeOJ(c), and o'. m(a) is invoked, it is acceptable for
the argument to have static type A and for the result to have static type B. Therefore it
is sufficient to require that B' <: B (covariantly) and that A <: A' (contravariantly). This
is called method specialization on override: the result type B is specialized to B' and the
parameter type A is generalized to A', with the net effect that A~B is specialized to
A'~B ' .
There is another form of method specialization that happens implicitly by inherit-
ance. The occurrences of self in the methods of c can be considered of type InstanceTy-
peOJ(c), in the sense that all objects bound to self have type InstanceTypeOJ(c) or a
subtype of InstanceTypeOJ(c). When the methods of c are inherited by c', the same occur-
rences of self can similarly be considered of type InstanceTypeOJ(c'). Thus the type of
self is silently specialized on inheritance (covariantly!).

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

Another way to look at specialization is to consider methods as functions whose


first parameter is self; we call such functions pre-methods. In the case of the method m1
of c, this pre-method pml would have type (InstanceTypeOj(c)xAl)~Bl. This type is a
subtype of (InstanceTypeOj(c')xAl)~Bl' so pm1 has this type too by subsumption. Then
pm1 has the type of a legal pre-method for c', and it can be inherited by c'. More gener-
ally, an inheritable pre-method of type (InstanceTypeOj(c)xAl)~Bl has by subsumption
the type (InstanceTypeOj(c')xAl')~Bl' for any AI' <: Al and Bl <: Bl'. That is, the param-
eters, including self, may be specialized, and the results may be generalized.
Note that the argument and result inclusions we have derived for inheritance are
opposite to the ones for overriding: for inheritance, parameter types may be special-
ized, and result types may be generalized; for overriding, parameter types may be gen-
eralized, and result types may be specialized. In any case, the sound rules for method
specialization, both for inheritance and for overriding, are a direct consequence of the
constraints on subtyping and subsumption.

2.8 Self Type Specialization


Method specialization adds flexibility to subclass definitions by allowing the result
type of a method to vary. Another opportunity for flexibility arises when the result
type of a method is, recursively, its class type. In this section we consider a language
construct devised for specializing such methods.
Class definitions are often recursive, in the sense that the definition of a class c may
contain occurrences of InstanceTypeOj(c). For example, we could have a class c contain-
ing a method m with result type InstanceTypeOj(c):
class c is
var x: Integer := 0;
method mO: InstanceTypeOj(c) is .. . self . .. end;
end;
subclass c' of c is
var y: Integer := 0;
end;
On inheritance, recursive types are, by default, preserved exactly, just as other
types are. For instance, for 0' of class c', we have that o'.mO has type InstanceTypeOj(c)
and not, for example, InstanceTypeOj(c'). In general, adopting InstanceTypeOj(c') as the
result type for the inherited method m in c' is unsound, because m may construct and
return an instance of c that is not an instance of c'.
Suppose, though, that m returns self, perhaps after modifying the field x. Then it
would be sound to give the inherited method the result type InstanceTypeOj(c'), since
self is always bound to the receiver of invocations. With this more precise typing, we
would avoid subsequent uses of typecase: limiting the result type to InstanceTypeOj(c)
constitutes an unwarranted loss of information.
24 REVIEW. OSjECf-ORlENTED FEATURES

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.

3.1 Object Types


In the original formulation of classes (in Simula, for example), the type description of
objects is intermixed with the implementation of methods. This situation conflicts with
the now widely recognized advantages of keeping specifications separate from imple-
mentations, particularly to enable separate code development within large teams of
programmers.
A relatively recent variation on the classical model addresses this problem. Sepa-
ration between object specification and object implementation can be achieved by
introducing types for objects that are independent of specific classes [19, 113]. This
approach is supported by Modula-3 and by other languages that provide both classes
and interfaces.
When we first discussed the type InstanceTypeOJ(cell), its meaning appeared rather
limited. That type seemed to indicate that all its elements were obtained by executing
new on the cell class, and therefore that they all had identical methods. Later, by the
combination of subclassing, method overriding, subsumption, and dynamic dispatch,
we obtained objects of type InstanceTypeOJ(cell) that had additional attributes and dif-
ferent method code. It is therefore peculiar that the type InstanceTypeOJ(cell) should
depend explicitly on an entity, the class cell, that describes some specific method code.
None of the code from class cell is necessarily found in members of InstanceTypeOJ(cell).
In genera\, elements of InstanceTypeOJ(cell) have in common only the type signa-
ture of the attributes specified in cell; this is sometimes called the object protocol for cells.

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
26 REVIEW. OBJECT -ORIENTED FEATURES

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.

3.2 Distinguishing Subclassing from Subtyping


The subtype relation was previously based on the subclass relation. When object types
are independent of classes, we must provide an independent definition. There are sev-
eral choices: whether subtyping is determined by type structure or by type names in
declarations, and in the former case what parts of the structure of types matter.
Structural subtyping (subtyping determined by type structure) has desirable prop-
erties, such as supporting type matching in distributed and persistent systems (15, 95) .
A disadvantage is the possibility of accidental matching of unrelated types. However,
one can avoid such accidents by imposing distinctions on top of structural subtyping
(95). In contrast, subtyping based on type names is hard to define precisely, and does
not support structural subtyping.
For our present purposes we use a particularly simple form of structural subtyp-
ing. We assume, for two object types 0 and 0', that:
0'<:0 if 0' has the same components as 0 and possibly more
where a component of an object type is the name of a field or a method and its associ-
ated type. So, for example, ReCell <: Cell.
This definition implies that object types are invariant in their component types,
although we could extend it to allow method specialization as discussed in Section 2.7.
When object types are defined recursively we need to be more careful about the defi-
nition of subtyping; we defer this discussion to Chapter 9 and beyond.
With this definition of subtyping, object types naturally support multiple subtyp-
ing, because components are assumed unordered. For example, consider the object
type:
ObjectType ReInteger is
var contents: Integer;
var backup: Integer;
method restoreO;
end;
Then we have both ReCell <: Cell and ReCeli <: Relnteger.
As a consequence of the new definition of subtyping we have:
(4) If c' is a subclass of c, then ObjectTypeOf(c') <: ObjectTypeOf(c).
This holds simply because a subclass can only add new attributes to a class, and
because we require that overriding methods preserve the existing method types.
28 REVIEW. OBJEcr-ORIENTED FEATURES

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.

3.3 Type Parameters


Type parameterization is a general technique for reusing the same piece of code at dif-
ferent types. It is becoming common in modern object-oriented languages, in part inde-
pendently of object-oriented features. We describe it here both because it is interesting
in its own right, and because it is used for the treatment of binary methods later in this
chapter.
In conjunction with subtyping, type parameterization can be used to remedy some
typing difficulties due to contravariance, for example in method specialization. Con-
sider the object types Person and Vegetarian, and assume Vegetables <: Food (but not vice
versa) (341:
ObjectType Person is

method eat(Jood: Food);


end;
ObjectType Vegetarian is

method eat(Jood: Vegetables);


end;
The intention is that a vegetarian is a person, so we would expect Vegetarian <: Person.
However, this inclusion cannot hold because of the contra variance on the argument of
the eat method. If we erroneously assume Vegetarian <: Person, then a vegetarian can be
subsumed into Person, and can be made to eat meat.
We can obtain some legal subsumptions betwecn vegetarians and persons by con-
verting the corresponding object typcs into type operators (functions from types to
types) puameterized on the type ofJood:
3. ADVANCED CLASS-BASED FEATURES 29

ObjectOperator PersonEating[F <: Food] is

method eat(food: F);


end;
ObjectOperator VegetarianEating[F <: Vegetables) is

method eat(food: F);


end;
The mechanism used here is called bounded type parameterization. IS The variable F is a
type parameter, which can be instantiated with a type. A bound such as F <: Vegetables
limits the possible instantiations of F to subtypes of Vegetables. So VegetarianEating[Veg-
etables] is a type; in contrast, VegetarianEating[Food] is not well-formed. The type Vege-
tarianEating[Vegetables] is an instance of Vegetarian Eating, and is equal to the type
Vegetarian.
There are no elements of operators such as Person Eating; there are only elements of
types PersonEating[F] for some given F. Therefore we do not have any inclusion relation
between the operators VegetarianEating and PersonEating. However, we do have that:
for all F <: Vegetables, VegetarianEating[F] <: PersonEating[F]
because, for any F <: Vegetables, the two instances are included by the usual rules for
subtyping. In particular, we obtain Vegetarian = VegetarianEating[Vegetables) <: Person-
Eating[Vegetables). This inclusion can be useful for subsumption: it asserts, correctly,
that a vegetarian is a person that eats only vegetables.
Related to bounded type parameters are bounded abstract types (also called partially
abstract types).16 Bounded abstract types offer a different solution to the problem of
making Vegetarian a subtype of Person. We redefine our object types by adding the F
parameter, subtype of Food, as one of the attributes:
ObjectType Person is
type F <: Food;

var lunch: F;
method eat(food: F);
end;

15 Languages that support bounded type parameters include Trellis/Ow\, Sather,


Eiffe\, PolyTOIL, and Rapide. Other languages, such as C++, support simple type
parameters without bounds.
16 Languages that support fOnTIS of bounded abstract types include Rapide, Modula-3
through opaque types at the module level, and Beta through virtual classes [85].
30 REVIEW. OBJECT-ORIENTED FEATURES

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.

3.4 Subc1assing without Subtyping


We have seen in Section 3.2 how the partial decoupling of subtyping from subclassing
increases the opportunities for subsumption. Another approach has emerged that
increases the potential for inheritance by further separating subtyping from subclass-
ing. This approach abandons completely the notion that subclassing implies subtyping
(property (4)), and is known under the name inheritance-is-not-subtyping [52). It is
largely motivated by the desire to handle contravariant (argument) occurrences of S.elf
so as to allow inheritance of methods with arguments of type Self; these methods arise
naturally in realistic examples. The price paid for this added flexibility in inheritance
is decreased flexibility in subsumption. When Self is used liberally in contravariant
positions, subclasses do not necessarily induce subtypes.
Consider two types Max and MinMax for integers enriched with min and max
methods. Each of these types is defined recursively:
ObjectType Max is
var n: Integer;
method max(other: Max): Max;
end;
ObjectType MinMax is
var n: Integer;
method max(other: MinMax): MinMax;
method min(other: MinMax): MinMax;
end;
3. ADVANCED CLASS-BASED FEATURES 31

Consider also two classes:


class maxClass is
var n: Integer := 0;
method max(other: Self): Self is
if self.n > other.n then return self else return other end;
end;
end;
subclass minMaxClass of maxClass is
method min(other: Self): Self is
if self.n < other.n then return self else return other end;
end;
end;
The methods min and max are called binary because they operate on two objects: self
and other; the type of other is given by a contravariant occurrence of Self [36]. Notice that
the method max, which has an argument of type Self, is inherited from maxClass to min-
MaxClass.
Intuitively, the type Max corresponds to the class maxClass, and MinMax to min-
MaxClass. To make this correspondence more precise, we must define the meaning of
ObjectTypeOf for classes containing occurrences of Self, so as to obtain ObjectType-
Of(maxClass) = Max and ObjectTypeOf(minMaxClass) = MinMax. For these equations to
hold, we map the use of Self in a class to the use of recursion in an object type. We also
implicitly specialize Self for inherited methods; for example, we map the use of Self in
the inherited method max to MinMax. In short, we obtain that any instance of maxClass
has type Max, and any instance of minMaxClass has type MinMax.
Although minMaxClass is a subclass of maxClass, MinMax cannot be a subtype of
Max. Consider the class:
subclass minMaxClass' of minMaxClass is
override max(other: Self): Self is
if other.min(self) = other then return self else return other end;
end;
end;
For any instance mm' of minMaxClass' we have mm' : MinMax. If MinMax were a sub-
type of Max, then we would have also mm': Max, and mm'.max(m) would be allowed
for any m of type Max. Since m may not have a min attribute, the overridden max
method of mm' may break. Therefore:
MinMax <: Max does not hold
Thus subclasses with contravariant occurrences of Self do not always induce subtypes.
32 REVIEW . OBJECT-ORIENTED FEATURES

3.5 Object Protocols


Even when subclasses do not induce subtypes, we can find a relation between the type
induced by a class and the type induced by one of its subclasses. It just so happens that,
unlike subtyping, this relation does not enjoy the subsumption property. We now
examine this new relation between object types.
In the example of Section 3.4, we cannot usefully quantify over the subtypes of Max
because of the failure of subtyping. A parametric definition such as:
ObjectOperator P[M <: Max] is ... end;
is not very useful; we can instantiate P to P[Max], but P[MinMax] is not well-formed.
Still, any object that supports the MinMax protocol, in an intuitive sense, also sup-
ports the Max protocol. There seems to be an opportunity for some kind of subprotocol
relation that may allow useful parameterization. In order to find this subprotocol rela-
tion, we introduce two type operators, MaxProtocol and MinMaxProtocol:
ObjectOperator MaxProtocol(X] is
var n: Integer;
method max(other: X): X;
end;
ObjectOperator MinMaxProtocol(X] is
var n: Integer;
method max(other: X): X;
method min(other: X): X;
end;
Generalizing from this example, we can always pass uniformly from a recursive type
T to an operator T-Protocol by abstracting over the recursive occurrences of T. The oper-
ator T-Protocol is a function on types; taking the fixpoint of T-Protocol yields back T.
We find two formal relationships between Max and MinMax. First, MinMax is a
post-fixpoint of MaxProtocol, that is:
MinMax <: MaxProtocol(MinMax]
Second, let -<: denote the higher-order subtype relation between type operators:
P -<: P' iff PIT] <: P'(T] for all types T
Then the protocols of Max and MinMax satisfy:
MinMaxProtocol -<: MaxProtocol
Either of these two relationships can be taken as a basis for the notion of subprotocol:
S subprotocol T if S <: T-Protocol[S]
or
S subprotocol T if S-Protocol -<: T-Protocol
3. ADVANCED CLASS-BASED FEATURES 33

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.

4.1 Objects without Classes


Object-based languages are refreshing in their simplicity, compared with even the sim-
plest class-based languages. In their minimal form, as in Emerald and in simple clo-
sure-based encodings (11), they support only the notions of objects and dynamic
dispatch. When typed, they support only object types, subtyping, and subsumption.
The basic characteristics of object-based languages are the absence of classes and
the existence of constructs for the creation of individual objects. Since there are no
classes it is natural to introduce object types immediately, and to separate object inter-
faces from object implementations. For example, we may have:
ObjectType Cell is
var contents: Integer;
method getO: Integer;
method set(n: Integer);
end;
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;
This object definition creates a single object of type Cell and names it cell.

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
36 REVIEW . OBJECT-ORIENTED FEATURES

Even without classes, we can generate a uniform collection of objects by a proce-


dure that, whenever invoked, returns a new object. Such a procedure can have param-
eters, for example to initialize fields:
procedure newCell(m: Integer): Cell is
object cell: Cell is
var contents: Integer := m;
method getO: Integer is return self.contents end;
method set(n: Integer) is self.contents := n end;
end;
return cell;
end;
var cellInstance: Cell := newCell(O);
Such object-generating procedures are uncannily similar to Simula classes, which
also have parameters and an executable body. The role of new is taken over by proce-
dure call.
The main insight of the object-based model is that class-based notions need not be
assumed, but instead can be emulated by more primitive notions. Moreover, these
more primitive notions can be combined in more flexible ways than in a strict class dis-
cipline. A similar reduction of classes to objects will appear in our formal treatment of
object calculi.

4.2 Prototypes and Clones


As we have just seen, procedures may be used for generating objects. However, it may
be difficult or inconvenient to anticipate all the possible ways in which objects should
be parameterized. The so-called prototype-based languages adopt a different approach
to object generation. Instead of parameterizing objects beforehand, they generate stock
objects from prototypical objects, and customize the stock objects later [32, 79).
In class-based languages, object descriptions provide the templates from which
object instances are generated. In prototype-based languages, instead, concrete and
fully functional instances are built first. Some of these instances may be later inter-
preted as canonical representatives (prototypes) of classes of instances. Additional
instances are derived from the prototypes. There is no entity that represents a class of
instances, other than by convention .
The basic mechanism used for instantiating prototypes is cloning. Cloning pro-
duces a shallow copy of an object; that is, a copy of the outermost object structure, shar-
ing all the attribute values of the original object, but with independent state.
var cellC/one: Cell := clone cellInstance;
Cloning is a bit like new, but operates on objects instead of classes. Any object can
act both as an instance and as a prototype for further cloning. An object in the role of a
4. OBJECT-BASED LANGUAGES 37

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;

18 There is an equivalent of InstanceTypeOf{c) for prototype-based languages. Let


DescendentOf{o) be the collection of the objects cloned, directly or indirectly, from o.
The concept of DescendentOfis used in Cecil to constrain the parameters of methods
at run-time.
19 Method update is directly supported by some languages (Beta, NewtonScript,
Obliq, Kevo, Gamet). Other languages allow dynamically removing and adding
attributes (Self, Actl), or replacing groups of methods (Self, Ellie), much to the
same effect.
38 REVIEw. OBJECT-ORlENTED FEATURES

method set(n: Integer) is


let x = self.getO;
self.restore := method 0 is self.contents := x end;
self.contents := n;
end;
method restoreO is self.contents := 0 end;
end;
Here the set method updates the restore method; there is no need for a separate backup
field.
In the rest of this chapter we discuss the various ways in which clones may be
derived from prototypes, and how clones may mutate.

4.3 Inheritance by Embedding and by Delegation


Simple object-based languages such as Emerald use procedures for generating objects
that share common behavior, but these languages have no inheritance mechanism.
Prototype-based languages also have a generation mechanism, plus a way of
updating individual objects. Cloning can be seen, in part, as inheritance (in the sense
of method reuse), and update can be seen as overriding. Cloning and update, however,
do not provide sufficiently flexible forms of inheritance, because they always preserve
the shape of a prototype. In general, we may want to inherit the behavior of an object
when building one of a different shape.
A natural inheritance mechanism to consider is the extension of existing objects
with new attributes. Object extension, however, is not a simple operation. One problem
is the potential for clashes of the additional attributes with existing (but unknown)
ones; careful handling is required in typed languages [43,61,70, 126). Another problem
is the necessity of modifying the representation of an object in order to extend it, while
maintaining the object's identity (which is often determined by the memory address of
the representation). Because of these difficulties, direct attribute extension is rarely pro-
vided as a language construct; it is either found in specialized object models [14, 16J, or
it is intended to be used in restricted contexts (12).20
An alternative to extending existing objects is to build new objects that include
some attributes of existing ones. This is in fact the most common inheritance mecha-
nism for prototype-based languages, and the one we discuss in detail.
There are two orthogonal aspects to inheritance in prototype-based languages:
obtaining the attributes of a donor object, and incorporating those attributes into a new
host object. Along these dimensions, prototype-based languages can be separated into

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)

Figure 4-1. Embedding.


40 REVIEW. OBJECT-ORIENTED FEATURES

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

object reCellImpl: ReCell extends cell is


var backup: Integer := 0;
method restoreO is self.contents := self. backup end;
end;
reCellImpl.set :=
method (n: Integer) is
self.backup := self.contents;
self.contents := n;
end;
This code works because, with embedding, method update affects only the object to
which it is applied.

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 ....

IZet (code for IZet)


parent link
set (code for set)

aReCell. •
contents 0
backup 0
set (new code for set)
restore (code for restore)

Figure 4-2. Delegation.

As a first attempt towards a proper definition of reCellImp in delegation style, we


define reCellImp' as a child of cell:
4. OBJECf-BASED LANGUAGES 43

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 child of cell is
var backup: Integer := 0;
override set(n: Integer) is
self.backup := self.contents;
delegate cell.set(n);
end;
method restoreO is self.contents := self. backup end;
end;
Instead of writing super.set(n) we now write delegate cell.set(n). The meaning of
delegate cell.set(n) is to execute the set method of cell with self bound to the current self.
The difference between delegate and embed is that the former obtains the method
from the parent at the time of invocation, whereas the latter obtains it earlier, at the
time of object creation.
This definition of reCellImp' is similar to the definition of reCellImp for embedding,
in Section 4.4. The meaning is, however, different: reCellImp' does not contain a copy of
the attributes of cell, but instead it contains a parent link. Because of parent links, the
attributes of the cell object are shared by all its children. Therefore the preceding defi-
nition of reCellImp' is wrong: a single contents field becomes shared by all children of
cell and, in particular, by all clones of reCellImp'.
A better definition of a restorable cell would include a local copy of the contents
field, overriding the contents field of the parent. Hence we define reCellImp as follows:
object reCellImp: ReCell child of cell is
override contents: Integer := cell.contents;
var backup: Integer := 0;
override set(n: Integer) is
self. backup := self.contents;
delegate cell.set(n);
end;
method restoreO is self.contents := self. backup end;
end;
A crucial aspect of delegation inheritance is the interaction of parent links with the
binding of self. On an invocation of reCellImp.getO, the get method is found only in the
parent cell, but the occurrences of self within the code of get refer to the original
44 REVIEW . OBJECT-ORIENTED FEATURES

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

4.6 Embedding versus Delegation


In embedding inheritance, a freshly created host object contains copies of donor
attributes. Access to the inherited donor attributes is then no different from access to
original attributes.
The situation is not so simple in delegation inheritance, where a host object con-
tains links to external donor objects. During method invocation, the attribute-lookup
procedure must preserve the binding of self to the original receiver, even while follow-
ing the donor links. This requirement complicates both the implementation and the for-
mal modeling of method lookup.
In class-based languages the embedding and delegation models are normally
equivalent. In object-based languages they are distinguishable. In particular, the shar-
ing of donors typical of delegation can be exposed in several ways.
• In delegation, donors may contain fields, which may be updated; the changes
are seen by the inheriting hosts.
• Similarly, the methods of a donor may be updated, and again the changes are
seen by the inheriting hosts.
• It is often permitted to replace a donor link with another one in an object; then
all the inheritors of that object may change behavior.
• Cloning is still taken to perform shallow copies of objects, without copying the
corresponding donors. So all clones of an object come to share its donors and
therefore the mutable fields and methods of the donors.
Thus embedding and delegation are two fundamentally distinct ways of achieving
inheritance with prototypes; interesting languages exist that explore both possibilities.
Delegation is the most common of the two. An advantage of delegation is conve-
nience in performing pervasive changes to all inheritors of an object. This mechanism
for change fits well with an approach to programming where language and environ-
ment are integrated, and where program execution and development are unified.
Arguments have been proposed in favor of embedding over delegation [59, 117].
Delegation can be criticized because it creates dynamic webs of dependencies that lead
to fragile systems. Embedding is not affected by this problem because objects remain
autonomous. In embedding-based languages such as Kevo and Omega, pervasive
changes are achieved even without donor hierarchies.
The space efficiency obtained by sharing is one of the main motivations for dele-
gation mechanisms [114] . On the other hand, it has been argued that space efficiency,
while essential, is best achieved behind the scenes of the implementation [32, 59] . Even
delegation-based languages optimize cloning operations by transparently sharing
structures [SO]; the same techniques can be used to optimize the space utilization of
embedding-based languages [59, 118}.
In conclusion, the question of what is the best model of inheritance for object-based
languages is still open.
46 REVIEW. OBJECT-ORIENTED FEATURES

4.7 Dynamic Inheritance and Mode-Switching


Delegation inheritance is called static when parent links are fixed for all time, and
dynamic when parent links can be updated dynamically. In the delegation-based lan-
guage Self, for example, a parent link can be declared as variable; then the parent of an
object can be dynamically replaced with any another object (Figure 4_3).23 Dynamic
delegation is also called dynamic inheritance.

old parent new parent


aCelle ~

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)

Figure 4-3. Reparenting.

Although dynamic inheritance is in general a dangerous feature, it enables rather


elegant and disciplined programming techniques. In particular, mode-switching is the
special case of dynamic inheritance where a parent is replaced with an object having
the same attributes. 24 Mode-switching provides a way of modularly switching the
behavior of an object from one mode or role to another [119, 121], and has been proposed
also for class-based languages [119).
As an instance of mode-switching, consider an object representing a window with
an iconized mode and an open mode. The window canvas size and the icon picture are
preserved across iconization and deiconization operations. In both modes the window
records the pictures drawn into it. The window reacts differently to some operations
depending on the mode; for example, drawing primitives may switch between paint-
ing on-screen and off-screen. Switching between modes is obtained by reparenting;
each parent encapsulates the state and behavior typical of a single mode.

23 In contrast, Cecil is a delegation-based language that supports only static delega-


tion. This is due at least in part to the desire to statically type that language,
although some forms of dynamic inheritance are compatible with static typing.
24 When the parent hierarchy is used as a hierarchical name space for storing pro-
grams, as in the Self environment [12], there is no necessary relation between an old
parent and a new one.
4. OBJECT-BASED LANGUAGES 47

In principle, one could achieve the same mode-switching behavior by inserting a


flag in the code of each method; flipping the flag would then change the mode. How-
ever, this solution may become unwieldy when flags and modes multiply and interact.
Moreover, the flag solution relies on the closed-world assumption that all present and
future modes are known. With the reparenting technique, new modes can be added
modularly, without modifying existing code.
Mode-switching, as used in delegation-based languages, rarely relies on the full
power of dynamic reparenting. Most often it involves updating the parent links of indi-
vidual leaf objects, not of shared parents. In this case, the mode-switching technique is
not sensitive to the difference between delegation and embedding. In embedding-
based languages, some clean and interesting forms of dynamic inheritance and mode-
switching can be obtained just by attribute update.
As an example of mode-switchin& we can give a new parent, cell' : Cell, to the
object reCellImp of Section 4.5 (d. the language Self):
reparent reCellImp to cell';
This means that the parent link of reCellImp is modified so that cell' replaces cell. A call
to reCellImp.getO now invokes the get method of cell' instead of the one of cell.
An effect similar to that of mode-switching can be obtained by method update. For
example, in the embedding model of Section 4 .4, the reparenting of reCellImp to cell' can
be written, in a somewhat clumsy but explicit form (d. Ellie):
reCellImp.get :=
method 0: Integer is return embed cell'.getO end;
reCellImp.set :=
method (n: Integer) is
self.backup := self.contents;
embed cell'.set(n);
end;
Thus method update in an embedding model has the ability to emulate some common
applications of dynamic inheritance, including ones that are based on delegation.

4.8 Traits: From Prototypes back to Classes?


Prototypes and clones were initially intended to replace classes and instances. Several
prototype-based languages, however, seem to be moving towards a more traditional
approach based on class-like structures. Delegation-based languages like Self and Cecil
have evolved a distinction between traits objects, prototype objects, and normalobjects. 25

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). ~

Figure 4-4. Traits.

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.

4.9 Types for Object-Based Languages


Many object-based languages rely on dynamic features, such as dynamic delegation,
that are hard to capture fully in a static type system. As with untyped procedural lan-
guages, this extreme plasticity has methodological drawbacks that can be remedied by
the addition of appropriate type systems. A few typed prototype-based languages are
now emerging.
The typings associated with prototype-based languages such as Cecil and Omega
strongly restrict the dynamic features, to the point that there is not much difference
between those typings and the ones used for class-based languages. Therefore, much
of the discussion of types for class-based languages, carried out in Chapters 2 and 3,
applies to statically typed object-based languages.
However, some peculiar typing issues remain. First, mode-switching, as opposed
to unrestricted dynamic inheritance, can be typed rather easily.27 Second, the treatment
of method specialization and Self types for object-based languages requires consider-
ation.
We have seen in Sections 2.7 and 2.8 how method specialization and covariant Self
work in class-based language. Contravariant Self is more problematic; in Section 3.5
we had to recast recursive types as protocols and to abandon subsumption.
In object-based languages with dynamic inheritance, even covariant Self is reason
for concern, because methods may be updated (e.g., for mode-switching). It may come
as a surprise, then, that it is still possible to allow updating methods with result type
Self, in the presence of subsumption. Contravariant Self must again be treated via pro-
tocol mechanisms. We discuss these issues extensively in Parts II and III.

27 The possibility of typechecking mode-switching in object-based languages seems to


be a novel idea we introduce; mode-switching is ruled out in Cecil and Omega.
5 MODELING OBJECT-ORIENTED LANGUAGES

In the previous chapters we have presented a relatively unbiased description of a large


(although not exhaustive) collection of object-oriented features. We now begin to dis-
cuss our way of modeling those features.

5.1 Reduction to Basic Mechanisms


In our review of object-oriented languages we have examined differences and similar-
ities between various object-oriented features. It should be apparent that, despite all
the variations, many characteristics of class-based and object-based languages are just
different presentations of a few general ideas. A natural question is whether these dif-
ferent presentations can be unified into a single conceptual and formal framework.
We can imagine two possible ways of encompassing the variety of object-oriented
languages within a single framework. One approach is to create an object model so
general that it can express directly all the conceivable variations of object classification
and inheritance. Although such a model could probably be built, it would not be very
useful because of either its complexity or its excessive generality. A different approach
is to create a simple model that is flexible enough to represent more complex notions,
but that does not directly embody any particular one. This is the approach that has
resulted in a better understanding of procedural languages, via A-calculi. This is also
the approach that we adopt for object-oriented languages in this book. As we discuss
later in Section 6.7 and Chapter 18, A-calculi are not completely satisfactory as a foun-
dation for object-oriented languages. By substituting object calculi for A-calculi, we
obtain the desired modeling power.
The object calculi that we develop are formalisms at the same level of abstraction
as A-calculi, but based exclusively on objects rather than functions. Much as in the
development of A-calculi, we start with a minimum untyped kernel and enrich it with
derived constructs and with increasingly sophisticated type systems until language
features can be realistically modeled. In the course of this book we investigate untyped,
simply-typed, and polymorphic calculi, considering both functional and imperative
execution models. A common kernel of object primitives provides conceptual unity to
the whole family of calculi.
Object calculi can be seen as the result of reducing class-based and object-based
languages to basic mechanisms. Class-based languages integrate many ideas into a sin-
gle construct: the class. Much of the effort in understanding and analyzing class-based
languages goes into distinguishing the contributions of those ideas. Object-based lan-
guages decompose class-based features in order to simplify them, and reconstruct

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
52 REVIEW. OBjECf -ORIENTED FEATURES

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 .

5.2 The Role of Method Update


In our search for minimal object calculi, it seems natural to take objects to be collections
of methods. Fields are important too, but they can be seen as a derived concept; for
example, a field can be viewed as a method that does not use its self parameter. The
hiding of fields from public view has been advocated as a means of concealing repre-
sentation choices, and thereby allowing flexibility in implementation; identifying fields
with methods confers much of the same flexibility. When fields and methods are iden-
tified it is trivial to convert one into the other, conceptually turning passive data into
active computation, and vice versa.
The unification of fields with methods has the advantage of simplicity: both objects
and object operations assume a uniform structure. In contrast, the separation of fields
from methods induces a corresponding separation of object operations, and leads to
the implicit or explicit splitting of self into two components. Unifying fields with meth-
ods gives more compact and therefore more elegant calculi.
This unification, however, has one debatable consequence. The natural operation
on methods is method invocation, and the natural operations on fields are field selec-
tion and field update. By unifying fields with methods, we can collapse field selection
and method invocation into a single operation. To complete the unification, though, we
are led to generalize field update to method update. 28 At the lowest level of formaliza-
tion, the choice is therefore between distinguishing methods from fields or adopting
method update. We choose the latter.
The reliance on method update is one of the most unusual aspects of our formal
treatment, since this operation is not so common in programming languages. How-
ever, method update can be seen as a form of dynamic inheritance: it supports the
dynamic modification of object behavior, allowing objects, in a sense, to change their
class dynamically. Thus method update gives us an edge in modeling object-based
constructs, in addition to allowing us to model the more traditional class-based con-
structs where fields and methods are sharply separated.
A further justification for method update can be found in the desire to tame
dynamic inheritance. Dynamic inheritance is a unique feature of object-based lan-

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].

5.3 The Scope of this Book


In this book, we aim to cover a wide, but not universal, range of object-oriented con-
structions. We consider class-based and object-based languages, both functional and
imperative, typed and untyped.
In previous chapters, we have identified embedding and delegation as two funda-
mental ideas underlying many object-oriented features. In our formalization of objects,
we choose embedding over delegation as the principal object-oriented paradigm. This
approach is unconventional, but we have found that it yields the simplest formal sys-
tems and, as we shall see, it allows us to represent many features.
For example, our calculi can model classes well, although they are not class-based
(since classes are not built-in). They can also model embedding-based features directly,
including dynamic inheritance in the form of mode-switching. As for delegation-based
features, our calculi can model traits just as easily as classes, and they can model
dynamic delegation based on traits. Interpreting traits as ordinary objects, though,
would require significant formal complications, because of the complexity of method
lookup in delegation. Therefore we cannot account easily for this particular aspect of
certain delegation-based languages. At any rate, the use of traits as objects does not
seem to be essential, and is gradually being abandoned, particularly in typed lan-
guages [49] .
There are other aspects of object-oriented programming that we do not consider in
this book. In particular, our formal treatment is restricted to sequential languages and
to languages where each method is associated with a unique object (single-dispatch)
rather than multiple objects (multiple-dispatch).
The exclusion of certain execution models is not very important for our purposes.
In studying the typing properties of object-oriented languages, the execution model,
whether functional, relational, imperative, or concurrent, is largely irrelevant. We
demonstrate this by treating both functional and imperative calculi with similar type
structures. There is evidence that the same type structures can also be adapted to rela-
tional languages [13] and to concurrent languages [20] . The execution models of func-
tional and imperative languages are of course different, and we treat them separately.
Although we develop imperative calculi extensively, our principal emphasis is on
functional calculi. This may seem odd, since one of the key ideas of object-oriented pro-
gramming is the hiding of private state, a very non-functional notion. Moreover, in
simple situations, typing for imperative models is easier, because the effects of meth-
ods need not be reflected in result types. However, every imperative language contains
54 REVIEW. OBJEa-ORIENTED FEATURES

a functional language, hence the typing of imperative programs is at least as hard as


that of functional programs. In realistic situations, imperative models exhibit all the
typing problems of functional models. Therefore it is sensible to study functional cal-
culi; the additional type rules peculiar to imperative calculi do not cause significant
complications.
The restriction to Single-dispatch languages is more significant. Multiple-dispatch
features have a large impact on the type structure as well as the semantics of object-ori-
ented languages [48]. The techniques required by the two flavors of dispatch are so dif-
ferent that we do not attempt to treat them coherently. Currently, the large majority of
object-oriented languages are based on single-dispatch. Among the typed object-ori-
ented languages, only a few experimental ones adopt multiple-dispatch (49).
As we proceed with the formal treatment of objects in the following chapters, we
return periodically to the problem of modeling various features of object-oriented lan-
guages.
PART I
Untyped and First-Order Calculi
6 UNTYPED CALCULI

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.

6.1 Object Primitives


Like the pure A-calculus, our basic calculus of objects, the <;-calculus, consists of a min-
imal set of syntactic constructs and computation rules. In this section we explain its
primitives informally.

6.1.1 The Primitives of the <;-Calculus


Objects are the only computational structures in our calculus. An object is simply a col-
lection of named attributes; all attributes are methods. Each method has a bound vari-
able that represents self and a body that produces a result. The only operations on
objects are method invocation and method update. Except for variables, there is no
other construct in the calculus.
The next table summarizes the notation we use for objects and methods:
Objects and methods

<;(x)b method with self parameter x and body b


[/1=<;(XI)bl , ... , In=<;(xn)bnl object with n methods labeled II, ... ,In
0./ invocation of method I of object a
o.I~<;(x)b update of method I of object a with method <;(x)b

In more detail, an object [11=<;(XI)b l , ... , In=<;(xn)bnl is a collection of components


li=<;(Xi)b i, for distinct labels Ii and associated methods <;(xi)bi, for iEl .. n; the order of these
components does not matter. The object containing a given method is called the
method's host object. The letter <; (sigma) is used as a binder for the self parameter of a
method; <;(x)b is a method with self parameter x, to be bound to the host object, and
body b.

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
58 PART I. UNTYPED AND FIRST-ORDER CALCUU

A method invocation is written 0.1, where I is a label of o. The intent is to execute


the method named I of 0 with the object 0 bound to the self parameter, and to return the
result of the execution.
A method update is written o.I~C;(y)b. The semantics of update is functional: an
update produces a copy of 0 where the method I is replaced with C;(y)b. In order to make
the formal treatment easier at first, we defer investigating imperative update until
Chapter 10.
For example, the following is an object that contains two methods:
[il=C;(Xl)[), 12=C;(X2)x2. l lj
The first method, named 11, returns an empty object []. The second method, named 12J
invokes the first method through the self parameter X2. Less trivial examples appear in
Section 6.5.
In addition to methods, we often discuss fields. Fields are not part of the calculus,
but a method that does not use its self variable can be regarded as a field. For emphasis,
a method that uses its self variable is then called a proper method. With these conven-
tions, method invocation doubles as field selection, and method update doubles as
field update. Note that it is possible to update a field with a proper method, transpar-
ently converting passive data into active code, and conversely it is possible to update
a proper method with a field.
The following table summarizes our terminology for object attributes and object
operations:
Terminology
object attributes
fields methods
object operations I selection field selection method invocation
I update field update method update

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.

6.1.2 The Primitive Semantics of the c;-Calculus


The execution of a c;-calculus term can be expressed as a sequence of reduction steps.
We call this the primitive semantics: it represents as simply and directly as possible the
intended semantics of objects.
In order to define the primitive semantics, we introduce the following notation.
Notation
• We use indexed notation of the form <l>i i.1..n to denote sequences <1>1, ... , <1> •.
• The notation b >+ C means that b reduces to c in one step.
6. UNTYPED CALCULI 59

• The substitution of a term c for the free occurrences of x in b is written b{xf-4


Substitution is defined precisely later on.
The reduction steps for c;-calculus operations are as follows.
Primitive semantics
Let 0 :: [Li=C;(Xi)bi iE1..n) (Ii distinct) object
o.lj =- bjKV-oH (jE1..n) invocation reduction
o.Ij~C;(y)b =- [Ij=C;(y)b, Ii=C;(xi)bi iE(1..n Hj') (fEL.n) update reduction

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.1.3 Examples of Reductions


Our first example of reduction is for an object with a single method named I; the body
of this method is the empty object [). Hence the method is in fact a field, and method
invocation and update produce the effects expected of field selection and update. We
use "fE,," for "equal by definition" and "::" for "syntactically identical" .
let 01 fE" [L=C;(x)[]) then 01 .1 =- [Hxf-OlH :: [)
and ol.I~C;(X)OI =- [I=C;(x)ol)
With our notation for fields, we can write 01 as [I=[)) and Ol.l~C;(X)Ol as 01.1:=01.
Self-substitution is at the core of the primitive semantics of method invocation as
we have defined it. It is thus easy to program non-terminating computations without
explicit use of recursion:
let 02 fE" [l=C;(x)x.l) then
Self-substitution also allows a method to return or to modify self:
let 03 fE" [I=C;(x)x) then 03.1 =- x{n-03H :: 03
let 04 fE" [1=C;(y)(y.l~C;(x)x») then 04.1 =- (04.l~C;(X)x) =- 03
We place particular emphasis on the ability to modify self, as illustrated by this last
example. In class-based languages, it is common for a method to modify attributes of
self, although these attributes are fields and not proper methods. As indicated in Sec-
tion 5.2, we allow a method to update other methods of self, or even itself, in the spirit
of prototype-based languages. This feature does not significantly complicate the prob-
lems that we address. Method update is exploited in rather interesting examples that
seem difficult to emulate in other calculi and in class-based languages.
60 PART I. UNTYPED AND FIRSf-ORDER CALCULI

6.1.4 Other Primitives


We should stress that our choice of primitives is not without alternatives.
For example, we could have tried to provide operations to add and to remove
methods from objects, from which update could be defined [43]. However, we feel that
update is an important operation characterized by peculiar typing rules; we prefer to
study it directly and not to explain it away at an early stage.
Similarly, the choice of objects with a fixed number of components, instead of
extensible ones [1, 61, 92, 125], is a conscious one. Fixed-size objects are easier to handle,
particularly in the later stages of our type-theoretical development.
Finally, we do not provide an operation to extract a method from an object as a
function. As we shall see, such an operation is incompatible with object subsumption
in typed calculi. Methods are inseparable from objects and cannot be recovered as func-
tions; this consideration inspired the use of a specialized <;-notation instead of the
familiar A-notation for parameters.

6.2 The <;-Calculus


In this section we formalize the untyped object calculus just described. In later chap-
ters, this calculus is the basis for typed calculi.
We define a formal syntax, a reduction relation with an associated equational the-
ory, and an operational semantics. These definitions provide more precise and refined
formulations of the primitive semantics of the <;-calculus. For understanding the rest of
this chapter, however, it suffices to have an intuitive grasp of the primitive semantics.

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

(Eq x) (Eq Object) (Ii distinct)


r- bi H b;' 'v'iEl..n

(Eq Select) (Eq Update)


r- a H a' r- a H a' r- b H b'
r-a.l H a'.I I-- a.l~c;(x)b H a'.I~c;(x)b'

(Eval Select) (where a =[l;=~(xi)bi{xi) iEl..n])


jEl..n

(Eval Update) (where a = [l;=~(Xi)bi iE!..n])


jEl..n

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.

6.2.4 Operational Semantics


The reductions and equations studied so far do not impose any specific evaluation
order. We now define a reduction system for the closed terms of the c;-caJculus; this
reduction system is deterministic.
Our intent is to describe an evaluation strategy of the sort commonly used in pro-
gramming languages. A characteristic of such evaluation strategies is that they are
64 PART I. UNTYPED AND FIRsr-ORDER CALCULI

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 Select) (where v' == [1;=C;(Xi)bi{Xi) i.1.. nn


f- a -- v' f- bj{v'J -- v jEl .. n
f- a.lj -- 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---.

Proposition 6.2-3 (Soundness of weak reduction)


If f- a -- v, then a -- v and hence f- a H v.
o
This proposition can be checked with a trivial induction on the structure of the proof
that f- a -- v.
Further, -- is complete with respect to --, in the following sense:
6. UNTYPED CALCULI 65

Theorem 6.2-4 (Completeness of weak reduction)


Let a be a closed term and v be a result.
If a -- v, then there exists v' such that I- a v+ v'.
o
This theorem was proved by Mellies [87] via a standardization lemma [64].
Weak reduction in the ~-calculus is analogous to weak reduction in the A-calculus.
In the A-calculus, weak reduction proceeds by reducing the function part of an appli-
cation until it becomes an abstraction; then the argument is substituted into the abstrac-
tion either without evaluation, for call-by-name, or after evaluation, for call-by-value.
In the ~-calculus, the distinction between call-by-name and call-by-value is not so crisp.
We may say that we have adopted a call-by-name strategy in light of Theorem 6.2-4,
which holds for call-by-name but not call-by-value in the A-calculus.
In later chapters we consider v+ again and study its properties related to typing.
We also describe a relation analogous to v+ for an imperative calculus.

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.

6.2.6 Review of Notation


We summarize the relations between terms introduced so far. In addition, we use = to
denote an equality relation between terms that we do not fully formalize; the meaning
of = should always be clear from context.
66 PART I. UNTYPED AND FIRST -ORDER CALCULI

Relations between terms

a=b syntactic identity


(up to renaming of bound variables and reordering of object attributes)
a>-+b top-level one-step reduction
a-+b one-step reduction
a--b many-step reduction
f-a H b equality (convertibility)
f-c--v operational reduction (with c closed and v a result)
a=b equality, in an informal sense

6.3 Functions as Objects


It would be possible to incorporate the terms of the A-calculus in our object calculus.
However, having two similar variables binders (A and ~) in a small calculus seems
excessive. We could replace ~ with A, identifying methods with functions. We feel,
instead, that the ~-binders have a special status. First, they can be given a simple and
direct reduction semantics, instead of an indirect semantics involving both A-abstrac-
tion and application. Second, ~-binders by themselves have a surprising expressive
power; we show next that they are at least as expressive as A-binders.

6.3.1 A-terms as Objects


«
We define a translation »from A-terms to pure objects. The A-terms consist of vari-
ables, applications, and A-abstractions.
Translation of the untyped A-calculus

«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»)

Recall that p.arg:=q stands for p.arg;,c;,(y)q, for an unused y.


The idea of the translation is that an application «b(a)>> first stores the argument «a»
into a known place (the field arg) inside of «b», and then invokes a method of «b» that
can access the argument through self. For example:
«(A(X)X )(y)>>
-- ([arg=~(x)x.arg, val=c;,(x)x .arg).arg:=y).val
= y
6. UNTYPED CALCULI 67

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).

6.3.2 Default Arguments and Call-by-Keyword


The translation of the A-calculus can be extended to provide a natural interpretation of
A-terms with default parameters and with call-by-keyword. These extensions demon-
strate that the translation technique is robust.
We write A(x=c)b{x} for a function with a single parameter x with default c. We
write fa) for a normal application off to a, and f) for an application off to its default.
For example, (A(X=C)X)O = C and (A(x=c)x)(a) = a. The interpretation of A-terms with a
single default parameter is as follows.
Translation of default parameters
«A(x=c)b{x)» ~ [arg=«c», val=c;(x)«b{x}»Hx~x.argBJ
«b(a)>> ~ «b».«a» where p.q ~ (p.arg:=q).val
«bO» ~ «b».val
68 PART 1. UNTYPED AND FIRST-ORDER CALCULI

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.

6.5.1 Movable Points


A classic object-oriented example involves geometric points with a move method that
updates the point coordinates.

ongln1

=A
Ix = O,
mv_x = C;(s) A(dx) s.x := s.x+dxl
70 PART 1. UNTYPED AND FIRSf-ORDER CALCULI

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'

6.5.2 Backup Methods


Our second example is a simple illustration of the technique of storing self. We define
an object that is able to keep backup copies of itself, for example as an audit trail. This
object has a backup method, and a retrieve method that returns the last backup:
o ~
[retrieve = ~(Sl) 51,
backup = ~(S2) s2.retrieve * ~(Sl) 5u
(additional attributes)]
The initial retrieve method is set to return the initial object. Whenever the backup
method is invoked it stores a copy of self into retrieve. Note that backup stores the self 52
that is current at backup-invocation time, not the self 51 that will be current at retrieve-
invocation time. For example:
0' ~ o.backup
= [retrieve = ~(Sl) 0, .. . J

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.

6.5.3 Object-Oriented Natural Numbers


The technique of storing self, shown in the previous example, comes up in another
interesting situation. We define the object-oriented natural numbers, that is, objects
that respond to the methods iszero (test for zero), pred (predecessor), and 5UCC (succes-
sor), and behave like the natural numbers.
6. UNTYPED CALCULI 71

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

6.5.5 Storage Cells


Our final example is that of storage cells, like those described in Section 2.1 . Our first
cell has a contents field, initialized to 0, and methods to get and to set the contents. We
assume that clients do not update these methods.
myCell !
[contents = 0,
get = ~(s) s.contents,
set = ~(s) A(n) s.contents := nJ
A restorable cell, in addition, has a backup field for remembering the next-to-Iast
value to which the cell was set, and a method to restore the contents. The set method is
responsible for keeping the backup field up to date; the restore method simply copies the
backup field to the contents field .
myReCell !
[contents = 0,
get = ~(s) s.contents,
set = ~(s) A(n) (s .backup:= s.contents).contents := n,
backup = 0,
restore = ~(s) s.contents := s.backupJ
Next we define a restorable cell that does without a backup field, by taking advan-
tage of method update. The set method becomes responsible for keeping the restore
6 . UNTYPED CALCULI 73

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.

6.6 Traits, Classes, and Inheritance


The object-oriented notions of traits, classes, and inheritance, discussed in the Review,
are not explicit in our calculi. Here we discuss how these notions can be represented in
terms of pure objects.
To avoid ambiguity, we call pre-methods those functions that become methods
once embedded into objects. We take the point of view that inheritance means pre-
method reuse, and that traits and classes are collections of interdependent reusable
pre-methods. We make methods <;(x)b reusable by writing them first as functions A(x)b
(that is, as pre-methods), and then by repeatedly embedding these functions into
objects.

6.6.1 Representing Traits and Classes


A trait is intended as a modular fragment of object behavior, with the understanding
that multiple traits may be needed in generating individual objects. We represent a
trait as a collection of pre-methods. A class is intended as an object generator that is
self-contained, although it may have been assembled with contributions from other
classes. We represent a class as a collection of pre-methods together with a method
called new for generating new objects.
Our idea for representing traits is that if 0 == [li=<;(xi)b i iEl..n) is an object, then:
t £ [li=A(xi)b i iEl..n) (that is, [li=<;(z)A(xi)b i iEl..n] where z is not used)
can be seen as a trait for o. The pre-methods A(Xi)b i in the trait are fields that can be
extracted.
The trait t is complete in the sense that we can construct 0 from it. We could also
choose to collect the pre-methods of t into two or more smaller traits:
t1 £ [li=A(xi)b i iEI..P] t2 £ [li=A(xi)b i iEp+l..n]

From a collection of partial traits like t1 and t2 we can easily reconstruct t.


74 PART I. UNTYPED AND FIRST-ORDER CALCULI

Given a collection of traits, it is easy to write a function that generates an object


from those traits. This function applies the pre-methods of the traits to the self of the
object, thereby converting the pre-methods into methods. In the case of the single, com-
plete trait t we would write:
new £ A(z)[li=~(s)z.li(s) i€ln)
o £ new(t)
= [li=~(s)(A(xi)bi)(s) ieln)
= [li=~(xi)bi i€ln)
This particular implementation of new works correctly only for traits with labels
Ii i€ I..n. Therefore in order to create an object from a given trait, or collection of traits, we
must pick the appropriate version of new.
We can avoid the annoyance of selecting the appropriate version of new for a trait
by associating a new function with the trait. A convenient association is obtained by
adding new as a proper method of the trait. This gives us a class; a class is a complete
trait, plus the method new:
c £
[new=~(z)[ li=~(s )Z.li(S) ie I..n),
li=A(s )bi i€ In)
o £ c.new
= [li=~(xi)bi i€l..n)
Given any class c, the invocation c.new produces an object of the right kind. The code
for new is tedious to write, but can be uniformly generated.

6.6.2 Representing Inheritance


Inheritance consists of reusing pre-methods across traits and classes. For example, c'
below is defined by reusing all the pre-methods of c (namely c.li i€ln) and by adding
some more pre-methods (A(s)bk ken+ln+m). Informally, we say that c' is a subclass of c:
c' £
[new=~(z)[li=~(s )z.li(s) ieln+m),
li=c.li i eln,
IrA(s)bk ken+l..n+m)

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.

6.7 Interpretations of Objects


In order to avoid premature commitments, so far we have avoided any explicit encod-
ing of objects in terms of other notions. However, an inspection of the primitive seman-
tics of Section 6.1 reveals close similarities between objects and records of functions. It
is natural then to try to define objects in terms of records and functions. The correct def-
inition is, however, not evident. We describe two options that have been studied in the
literature, recasting them in our notation. Further options appear in Chapter 18.

6.7.1 The Self-Application Semantics


All implementations of standard (single-dispatch) object-oriented languages are based
on self-application. In the self-application semantics [73, 74), methods are functions,
objects are records, object selection is record selection plus self-application, and update
is simply update. We write (li=ai ifLn) for the record with labels Ii and fields ai; we write
a.li for record selection (extracting the Ii component of a), and a.lr=b for functional
update (producing a copy of a with the Ii component replaced by b).
Self-application semantics
Let 0 == [li=C;(Xi)b;{Xi} if!..n] (Ii distinct)
o ~ (li=A(Xi)b i if!..n)
o.li ~ 0.//0) biM (jE1..n)
o.lj~C;(y)b ~ o'/r=A(y)b [li=C;(y)b, l;=C;(Xi)b i if(LnHill (jEl ..n)

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.

6.7.2 The Recursive-Record Semantics


In view of these difficulties with typing, alternatives to the self-application semantics
have been investigated. One immediate idea is to try to "hide" the self parameters so
that their types do not appear in contravariant position. This can be achieved by the use
of recursive definitions, binding all the self parameters recursively to the object itself
[39,74,106). Note that C; is not interpreted as A in this semantics.

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

with the primitive semantics: p.ll = 5 P.l2 = 5


with the recursive-record semantics: p.ll = 5 p.l2 = 3
Therefore the recursive-record semantics is not adequate.
We can reason that the fixpoint operator has been used too soon, and that update
has no chance of working once recursion is frozen. In contrast, recursion in the self-
application semantics is open-ended: self-application occurs only at method invocation
time, not at object-construction time. More sophisticated variants of the recursive-
record semantics are discussed in Chapter 18.

6.7.3 Back to the Primitive Semantics


In summary, there exist some more or less faithful interpretations of objects. The best
available interpretations require sophisticated typing techniques that we have not yet
introduced; we resume our discussion of interpretations in Chapter 18.
In the core of this book, we take the primitive semantics as our "official" semantics
of objects. As we shall see, this approach has advantages: primitive objects provide a
useful abstraction. We will gain much insight by asking first what abstract properties
objects should exhibit, and by investigating later how these properties can be realized.
Many programming languages take objects as primitive too, but often accompany
them with additional constructs. In contrast, our calculi include a minimal number of
simple primitives from which more complex constructs can be derived.
7 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.

7.1 Formal Systems


We compose many of our typed systems from formal system fragments. Each fragment
is named I:!.s for an appropriate subscript s. These fragments, listed in Appendix A, can
be assembled to form various typed calculi, some of which are listed in Appendix B.
Each fragment consists of a set of related rules. Each rule has a number of premise
judgments above a horizontal line and a single conclusion judgment below the line.
Generalizing the conventions of Section 6.2.3, each judgment has the form E I- 5, for a
typing environment E and an assertion 5 whose shape depends on the judgment. For
now, we can assume that an environment E consists of a list of assumptions for vari-
ables, each of the form x:A. The empty environment is written f/l. A rule has the form:
(Rule name) (Annotations)
E1 I- 51 ... En I- 5 n
EI-5
In writing rules we use some abbreviations. A premise of the form "E, Ei I-
5i ViEl..n" is an abbreviation for n premises "E, Ell- 51 ... E, En I- 5 n" if n > 0, and if n
=0 for "EI- 0", which means that Eis well-formed. Instead, "jEt ..n" in the premise indi-
cates that there are n separate rules, one for each j.
Each rule has a name whose first word is determined by the conclusion judgment;
for example, rule names of the form "(Type ... )" are for rules whose conclusion is a
type judgment. When needed, conditions restricting the applicability of a rule, as well
as abbreviations used within the rule, are annotated next to the rule name.
A formal system is a set of rules. A derivation in a given formal system is a tree of
judgments. We use the notation:

Judg (rule)

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
80 PART I. UNTYPED AND FIRST-ORDER CALCULI

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.

7.2 The Object Fragment


We start with the formal system fragment, ':\'Ob, corresponding to object types. An
object type [li:Bi i<l..nllists the names and types of the methods of an object. It exhibits
only the result types Bi of methods: it does not explicitly list the types of c;-bound vari-
ables. The types of all these variables are equal to the object type itself, so no informa-
tion is missing. The terms of this fragment are similar to the corresponding terms of the
untyped c;-calculus, except for the addition of typing annotations for c;-bound variables.
As guide to our first fragment, the following table lists the relevant syntax; addi-
tional constructs are necessary to obtain a full calculus (e.g., variables). This syntax is
in fact implicit in the rules of ':\'Ob; for later fragments we do not display the syntax
explicitly.
Syntax fragment for ':\'Ob
A,B ::= types
[li: Bi i<l..nl object type (Ii distinct)
7. FIRST-ORDER CALCULI 81

a,b ::: terms


[l;=C;(Xi:Ai)b; i€l..n] object (Ii distinct)
a.1 method invocation
a.l~c;(x:A)b method update

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.

(Type Object) (Ii distinct)


EI-B; 'v'iE1 .. n
E I- (li:Bi ;€l..n]

(Val Object) (where A == [/i:Bi iel..n])


E, x;:A I- b;: Bi 'v'iEl..n
E I- (li:C;(Xi:A)bi i€J..n] : A

(Val Select) (Val Update) (where A == [Ii:Bi iEl..n])


E I- a : (li:Bi i€J..n] jE l..n El-a:A E,x:Al-b:B j jEl..n
E I- a.lj : Bj E I- a . lj~c;(x :A)b : A

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.

7.3 Standard First-Order Fragments


We can obtain a well-rounded typed version of the ~-calculus by adding to dOb two
standard fragments for variables and for a constant type. We also add a fragment for
typed functions, leaving their definability from typed objects for later discussions.
The fragment d x describes how to build environments, and how to extract the type
of a variable from an environment. The judgment E I- 0 asserts that E is a well-formed
environment. By XEdom(E) we indicate that x is defined in E.
!J. x
(Envf/l) (Env x) (Val x)
E I- A x;.dom(E) E', x:A, E" I- 0

¢I-o E, x:A I- 0 : , E" I- x : A


E', x A

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.

(Type Arrow) (Val Fun) (Val AppI)


Ef-A Ef-B E, x:A f- b: B E f- b : A~B E f- a : A
Ef-A~B E f- A(x:A)b : A~B E f- b(a) : B

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.1 A Divergent Term


Ob}, unlike F}, is not normalizing: there exist typable terms that diverge (do not termi-
nate). For example, the untyped term (1=<;(x)x./].1 of Section 6.1.3 can be annotated to
obtain the typed term [1=<;(x:(I:(lJ)x./].I, which is typable in ObI as follows.
(Env ~)
rrrr
(~I-O
~ I- [J (Type Object) with n = 0
[l :f)]
~ I- (Type Object) with n = 1
~, x:(l:f)] I- 0 (Env x)
~, x:(l:f)] I- x : [/:f)] (Val x)
~, x:[l:()) I- x.1 : [J (Val Select)
~ I- (1=<;(x:(I:(lJ)x ./J : (I:()) (Val Object) with n = 1
~ I- (1=<;(x:[I:(lJ)x.l].I : [J (Val Select)
Note how the rule (Val Object) enables us to assume that the self variable x has the type
[I:f)] when checking that the body x.I of the method I has the type [J.

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

7.5 Some Properties of ObI


In this section, we concentrate on Db}, studying its basic typing properties. Although
these properties are of some interest, the main purpose of this section is to introduce
ideas and techniques that are important in later chapters.

7.5.1 Unique Types


In Db}, every term that has a type has a unique type.
Proposition 7.5-1 (ObI has unique types)
If E f- a : A and E f- a: A' are derivable in Db}, then A == A'.
o
The proof is a trivial induction on the derivation of E f- a : A, and extends to FObl .
Unique typing is an obvious, fundamental property for Db}, but small perturba-
tions of the rules do not always preserve it. The property remains true if we omit the
type annotation for update, by changing a . lj~r;(x:A)b to a.lj~r;(x)b, and by adopting the
rule:
(Val Update' ) (where A =f/i: Bi iEl..n))
Ef-a:A E,x:Af-b : Bj jEl..n
E f- a.lj~r;(x)b : A

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

(Val Object') (where A;: [li:Bi i·l.."n


E, Xi:A I- bi : Bi 'ViE l..n
E I- [li=~(Xi)bi iE!..n] : A

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.

7.5.2 Subject Reduction


All the reduction relations of Section 6.2 can be extended to typed terms. Here we
extend the weak reduction relation vo+ of Section 6.2.4 to Obt terms. For this purpose
we simply ignore and carry along any type information. Much as in Section 6.2.4, a
result is a term of the form [li=~(Xi:A;)bi iEl .. n].
Operational semantics
(Red Object) (where v;: [li=C;(xi:Ailbi i.L"n

(Red Select) (where v';: [li=C;(xi:Ai)bi{xil i.I.."))


I- a vo+ v' I- bj(v'J vo+ v jE l..n
I- a.l; -- v

(Red Update)
I- a -- [li=~(Xi:Ai)bi iEt .. n] jEl..n

In Ob v reduction preserves types; technically this is called a subject reduction prop-


erty. We first state a standard substitution lemma, which can be checked by an easy
induction, and then prove subject reduction; the proof is simple and serves as an intro-
duction to similar arguments for more complex calculi.
Lemma 7.5-2 (Substitution)
If E, x:D, E' I- ::Hx} and E I- d : D, then E, E' I- gldD.
o
Theorem 7.5-3 (Subject reduction for Obt)
Let c be a closed term and v be a result, and assume I- c -- v.
If I1lI- c : C, then I1lI- v : C.
7. FIRST-ORDER CALCULI 87

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

Since Outcome(c) is defined, so is Outcome(a). Let 0 = Outcome(a). By hypothesis f/l f-


a.l j ::= <;(x:A)b : C. Then f/l f- a : A, and A equals C and has the form [lj:B, .. . ]. By induc-
tion hypothesis, f/l f- 0 : A. Therefore 0 has the form [1;=<;(x;:A;)b; ;'I..n] with jEl..n.
This implies that A must have the form [lj:B, I;:B; ;.(I..n)-(jl] . It follows that A; equals
A and f/l, x;:A f- b;:B; for all i. In addition, since f/l f- a.lj ;, <;(x:A)b : A, we obtain also f/l,
x:A f- b : B. Thus by (Val Object), III f- [lj=<;(x:A)b, 1;=<;(x;:A)b; ;E(l..n Hjl ] : A, that is, III f-
Outcome(a'!j ;' <;(x:A)b) : c.
o
7. FIRST-ORDER CALCULI 89

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.

7.6 First-Order Equational Theories


The equational theory of Ob1 was implicitly assumed in some of the previous discus-
sion; we now define it. The proof of soundness for this equational theory is postponed
until Chapter 14.

7.6.1 Equational Rules


We use a new judgment E f- b H C : A to assert that band c are equivalent when consid-
ered as elements of type A. The first two rules for this new judgment express symmetry
and transitivity; reflexivity will be obtained as a derived rule.

11=
(EqSymm) (Eq Trans)
Ef-aHb : A Ef-aHb : A Ef-bHc:A
Ef-bHa : A Ef-aHc:A

There is an obvious rule for variables: a limited form of reflexivity.

(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 Object) (where A == [1;:B, ,' 1.."))


E, Xi:A f- bi H bi: Bi V'iEl..n
90 PART I. UNTYPED AND FIRST-ORDER CALCULI

(Eq Select)
Ef-aHa ' :[Ii: Bi iel..nl jEl..n
E f- a.lj H a'.lj : Bj

(Eq Update) (where A:; [li:Bi iEl..n])


Ef-aHa' : A E,x:Af-bHb' : Bj jE1..n
E f- a.lj~~(x:A)b H a ' .lj~~(x:A)b' : A

(Eval Select) (where A:; [li:Bi iEl..n), a:; [li=C;(xi:A)bi(xil iEl..n))


E f- a: A jE l..n
E f- a.lj H bj{aJ : Bj

(Eva! Update) (where A :; [li: Bi iEl..n), a:; [li=C;(xi:A)b i iEl..n])


Ef-a : A E,x:Af-b:B j jEl..n

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

(Eval Beta) (Eval Eta)


E f- A(x:A)b[x\ : A-tB E f- a : A E f- b : A-+B xtdom(E)
E f- (A(x:A)b[x))(a) H bM : B E f- A(x:A)b(x) H b: A-tB

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}.

7.6.2 Equality and self


The simple equational theory of ObI is already quite interesting. In record calculi we
can safely assume that two records rand r' are equal if they have the same labels Ii iEl.. n
and if the values r·l i and r' .Ii are equal for all iEl..n. This property does not hold for
objects: two objects may give equal result for all their methods, and still be distinguish-
able. Consider the following definitions, where Nat is the type of natural numbers:
A ! [x:Nat, f:Natl
7. FIRST-ORDER CALCULI 91

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.

7.7 Functions and Fixpoints


The Ob1 calculus is sufficient to encode Fl along the lines sketched in Section 6.3. Hence
FOb1 has a built-in redundancy, although a very convenient one. The translation from
Fl to Ob 1 is shown below. Strictly speaking, this translation is defined on typing deri-
vations, but for simplicity we write it as a translation of type-annotated A-terms (that
is, of A-terms subscripted with their types). We write «E» for the translation of an envi-
ronment, «A» for the translation of a type, and «a»p for the translation of a term. Here p
is a function from variables to terms of Ob 1; j1S is the identity function, and p{yt-a} is the
modification of p that maps y to a.
Translation of the first-order A-calculus
P E VaT Ob1-teTm
~
j1S(x)g, x
(p{yt-a})(x) g, if x = y then a else p(x)
«j1S» g, j1S
«E, x:A» £ «E», x:«A»
«K» g, K
«A~B» £ [aTg:«A», val:«B»)
92 PART I. UNTYPED AND FIRST-ORDER CALCULI

«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]

It is not difficult to verify that the translation maps derivations in Fl to derivations in


ObI. Much as in Section 6.3, typed l3-reduction is satisfied by this encoding, but typed
11-reduction is not.
In the following chapters, we describe several extensions of Fl and ObI. Consider
a pair of extensions, Fe and Ob e. We say that Ob e can encode Fe when there exists a
translation mapping derivations of Fe that do not use the first-order 11 rule into deriva-
tions of Ob e . (This 11 rule is (Eval Eta) of Section 7.6.1.) In this sense, ObI can encode Fl.
In addition, ObI can encode an extension of Fl with a fixpoint operator fix A:
(A~A)~A for each A, in such a way that «fiXA(J)>> = «j(fixA(f»». All we need is the
fixpoint operator of Section 6.4, which is typable within ObI:
«fixA»P ~
[arg=~(x :«(A-?A)~A»)x.arg,
val=~(x:«(A~A)-?A»)«x.arg).arg:=x.val).val]

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

The standard rules for ll(x:A)b, namely:

E,x:A I-a : A E, x:A I- alxl : A


E I- ll(x:A)a : A E I- ll(x:A)alxl H a{ll(x:A)aH : A

are validated by this translation.


8 SUBTYPING

A characteristic of object-oriented languages is that an object can emulate another


object that has fewer methods, since the former supports the entire protocol of the lat-
ter. Conversely, a context that expects an object with a given method protocol can be
filled with an object that has an extended protocol. We call this notion subsumption: an
object can subsume another object that has a more limited protocol.
No object calculus can fully justify its existence without some notion of subsump-
tion. This criticism should first be directed to FOb}, studied in Chapter 7. We should
notice that, in accordance with the self-application semantics, there is no difficulty in
adding to FOb! a new operation that extracts a method from an object and returns it as
a function with parameter self. Without a good reason for ruling out this extraction
operation, an object calculus would be just an oddly restricted record calculus. As it
happens, the idea of extracting a method from an object is uncharacteristic of object-
oriented languages and, as we shall see shortly, this is precisely because this operation
is fundamentally incompatible with the typing of subsumption.
In this chapter we define a particular form of subsumption that is induced by a
subtype relation between object types. According to the rules for this relation, an object
that belongs to a given object type also belongs to any supertype of that type, and can
subsume objects in the supertype.
After extending our first-order calculi with subsumption, we prove a minimum-
types theorem and a subject reduction theorem. We consider classes again in the con-
text of the subtype relation. We also illustrate the formal differences between objects
and records, and introduce variance annotations that unify objects and records.

8.1 Sub typing


We begin with the basic rules of subtyping: reflexivity, transitivity, and subsumption.
It is also convenient to add a type constant, Top, that is a supertype of every type. The
judgment E r- A <: B asserts that A is a subtype of B in environment E.

(Sub Refl) (Sub Trans) (Val Subsumption)


Er-A E r- A <: B E r- B <: C E r- a : A E r- A <: B
E r- A <: A Er- A <: C Er-a:B

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
94 PART I. UNTYPED AND FIRST -ORDER CALCULI

(Type Top) (Sub Top)


EI-o EI-A
E I- Top EI- A <: Top

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'

(Sub Object) (Ii distinct)


E I- B; 'v'iEl..n+m
E I- [l;:Bi i'J..n+m] <: [li: B; i.I ..n]

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:

Fl<: ~ Fl U 6.<: u 6.<:....


FOb1<: ~ FOb l U 6.<: u 6.<: .... u 6.<:Ob
8. SUBTYPING 95

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.

8.3 Some Properties of Ob 1<:


In this section, we adapt the basic results about ObI to Ob l <:.

8.3.1 Minimum Types


With the addition of subsumption we have obviously lost the unique-types property
of ObI (see Section 7.5.1). However, a weaker property holds: every term of Ob}<: has
a minimum type (if it has a type at all). This property also holds for FI<~ and we believe
96 PART I. UNTYPED AND FIRSf-ORDER CALCULI

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

(Val Min Update) (where A'" [/i:Bi i<I.."])


El-a:A' S!SI-A'<:A E,x:Al-b:B;' S!SI-B;'<:Bj jE1..n
E I- a.lj~~(x:A)b : A

Typing in MinOb1<: is unique, as we show below. We can easily extract from


MinOb1<: a typechecking algorithm that, given any environment E and term a, com-
putes the type A such that E I- a : A if one exists.
The next three propositions are proved by easy inductions on the derivations of E
I- a : A in MinOb1<:.
Proposition 8.3-1 (MinOb1<: typings are Ob 1<: typings)
If E I- a : A is derivable in MinOb1<" then it is also derivable in Ob 1<:.
o
Proposition 8.3-2 (MinOb1<: has unique types)
If E I- a: A and E I- a: A' are derivable in MinOb 1<:, then A == A'.
o
Proposition 8.3-3 (MinOb1<: has smaller types than Ob 1<)
If E I- a : A is derivable in Ob1<:, then E I- a : A' is derivable in MinOb1<: for some
A' such that E I- A' <: A is derivable (in either system).
o
We obtain:
Proposition 8.3-4 (Ob1<: has minimum types)
In Ob1 <" if E I- a: A then there exists B such that E I- a: B and, for any A', if E I- a :
A' then E I- B < :A'.
8. SUBTYPING 97

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).

8.3.2 Subject Reduction


As in ObI (see Section 7.5.2), typing is consistent with reduction in Ob)<:. We start the
proof of consistency by stating two standard lemmas.
Lemma 8.3-5 (Bound weakening)
If E, x:D, E' I- g and E I- D' <: D, then E, x:D', E' I- g.
o
Lemma 8.3-6 (Substitution)
If E, x:D, E' I- g{x} and E I- d : D, then E, E' I- g«dl
o
Using these lemmas, we obtain a subject reduction theorem:
98 PART I. UNTYPED AND FIRST-ORDER CALCULI

Theorem 8.3-7 (Subject reduction for Ob I <)


Let c be a closed term and v be a result, and assume f- c -- v.
If ~ f- c : C, then ~ f- v : C.
Proof
The proof is by induction on the derivation of f- c -- v.
Case (Red Object)
This case is trivial, since c = v.
Case (Red Select)
Suppose f- a.lj -- v because f- a -- [h=C;(xj:Aj)bdxjl jEl..n) and f- bjK[lj=C;(xj:Aj)bdxjl jEl..nn
-- v. Assume that ~ f- a.lj : C. This must have come from an application of (Val
Select) with assumption ~ f- a: A where A has the form [lj:Bj, ... ), and with conclu-
sion ~ f- a.lj : Bj, followed by a number of subsumption steps implying ~ f- Bj <: C
by transitivity. By induction hypothesis, we have ~ f- [lj=C;(xj:Aj)bj{xjl jEl..n) : A. This
implies that there exists A' such that ~ f- A' <: A, that all A j equal A', that ~ f-
[lj=C;(xj:A')bdxjl JEI .. ") : A', and that~, xj:A' f- bj : Bj . By Lemma 8.3-6, it follows that ~
f- bjWj=C;(xj:A')bdxjl JEI .. "»> : Bj. By induction hypothesis, we obtain ~ f- v : Bj and, by
subsumption, ~ f- v : C.
Case (Red Update)
Suppose f- a.lj ~ C;(x:A)b -- [lj=C;(x:Aj)b, Ij=C;(xj:Aj)bj jE(l..n)-{j'] because f- a v-+
[lj=C;(xj:Aj)b j jel..n). Assume that ~ f- a.l j ~ C;(x:A)b : C. This must have come from an
application of (Val Update) with assumptions ~ f- a: A and~, x:A f- b : B where A
has the form [lj:B, ... ), and with conclusion ~ f- a.lj ~ C;(x:A)b : A, followed by a num-
ber of subsumption steps implying ~ f- A <: C by transitivity. By induction hypoth-
esis, we have f/l f- [lj=C;(xj:Aj)bj jel ..n] : A. This implies that Aj has the form [lj:B, Ij:B j
jE(l..n Hj ,j, that ~ f- Aj <: A, that A j equals A j, and that ~, xj:Aj f- bj : B j for all i. By

Lemma 8.3-5, it follows that~, x:Aj f- b: B. Therefore by (Val Object), ~ f- [lj=C;(x:Aj)b,


lj=C;(xj:Aj)b j jE(l..n Hj') : Aj. We obtain ~ f- [lj=C;(x:Aj)b, lj=C;(xj:Aj)bj jE(l..n)-{j,] : C by sub-
sumption.
o
As in ObI, the proof of subject reduction is simply a sanity check. It remains an
easy proof, with just one subtle point: notice that the proof would have failed if we had
defined (Red Update) so that f- a.lj ~ C;(x:A)b v-+ [lj=C;(x:A)b, Ij=C;(xj:Aj)bj jE(J..nHj'] with an
A instead of an Aj in the bound for x.

8.4 First-Order Equational Theories with Subtyping


We extend the equational theories of Section 7.6.1 to take subsumption into account.
Although subsumption is easy to incorporate, and the equational theory becomes
stronger, we still find some interesting difficulties in reasoning about self.
8. SUBTYPING 99

8.4.1 Equational Rules with Subtyping


With subtyping, two terms may be equal at some types but not at others. Therefore the
type information in the equality judgment E I- a H a' : A is essential.
The following equalities are associated with the t.<: fragment:

(Eq Subsumption) (EqTop)


E I- a H a': A E I- A <: B El-a : A El-b:B
E I-a Ha ' : B E I-a Hb: Top

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<:.

8.4.2 Equality, Subtyping, and self


In Section 7.6.2 we saw that the following terms band c cannot be equal at type A:
A ! [x:Nat,fNat)
100 PART I. UNTYPED AND FIRST-ORDER CALCULI

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].

8.5 Classes and Inheritance


We extend the untyped representation of classes discussed in Section 6.6 to account for
typing and subtyping. As in Section 6.6 we represent classes as collections of pre-meth-
ods with a new method. In FOb 1<: these classes acquire natural types; moreover, inher-
itance is in harmony with subtyping. After discussing the typing of classes, we give an
extensive example.

8.5.1 Representing Class Types and Inheritance


If A == [/i:Bi if1..n) is an object type, then:
C/ass(A) ~ [new:A, /i:A~Bi if1..n)
is the type of classes generating objects of type A. These classes have the form:
[new=~(z:C/ass(A»[/i=~(s:A)z./i(S) iE l"n),
/i=A.(s:A)bi i<l"nj
We would like to say that a class of type C/ass(A') with more pre-methods inherits
(or may inherit) from a class of type C/ass(A) with fewer pre-methods. It can be seen
easily that inheritance is not simply related to subtyping of class types: A' <: A implies
neither Class(A') <: Class(A) nor Class(A) <: Class(A'), since A and A' occur invariantly
in C/ass(A) and Class(A'). Therefore we define an ad hoc inheritance relation on class
types that captures the intuition of method reuse. When A and A' are object types, we
set:
C/ass(A') may inherit from Class(A) iff A' <: A
If Class(A') may inherit from C/ass(A), then A' and A have the forms A' == [/i:Bi
i<l"n+mj and A == [/i:Bi it1..nj. Thus A~Bi<: A'~Bi for iEl .. n, because ofthe contravariance
of function types. Hence, pre-methods of a class c : C/ass(A), having type A~Bi, may be
reused in assembling another class c' : Class(A'), by subsumption.
8. SUBTYFING 101

Whenever c' is defined by reassembling, extending, and modifying c we may infor-


mally say that c' inherits from c. For example, c' may replace the pre-method 11 of c,
inherit all the other pre-methods, and add some new ones:
c : Class(A) £
[new=~(z:Class(A))[li=~(s :A)z.li(s) iE 1..n],
li=A(s :A)bi iE1..n]
c' : Class(A ') £
[new=~(z:Class(A '))[li=~(s:A ')z.li(s) if1..n+m],
11 =A(s:A ')b 1,
Ij=c.lj jE2 .. n,
Ik=A(s:A ')bk kEn+1..n+m]

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.

8.5.2 Variations on Class Types


In Section 6.6 we introduced traits as possibly incomplete collections of pre-methods.
If A == [li:Bi if1..n] is an object type, then we say that:
for m ~n
is a trait type for it. An element of such a type is a trait.
Even when m < n, the pre-methods for Ii if1..m may still refer in a well-typed way to
the pre-methods for Ii iEm+1..n that are not provided . For example, in the trait
[11 = A(s:A) ... s.ln ... ], the invocation s.ln is well-typed. Thus a trait may implicitly refer
to its completion, and may be combined with other traits to form a complete collection
of pre-methods.
A trait, especially when incomplete, is also called an abstract class [115] . The term
"abstract" is used in the sense that concrete objects cannot be constructed from an
abstract class, because not all the necessary pre-methods may be available. Abstract
102 PART I. UNTYPED AND FIRSf-ORDER CALCULI

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

c : Class(A)pub,PubvPro has public methods I; i.Pub,


protected methods Ii i.Pro,
and private methods Ii ie/-PubvPro
Some subtleties of private method mechanisms are modeled by these definitions.
In a class of the form c/ass(A, ai iel)pub : Class(A)pub,pub, the pre-methods ai have types
A~Bi. Therefore any pre-method may refer through self (of type A) to all other meth-
ods, including the private ones. However, a private method can be accessed only from
the class in which it is declared, and not from objects generated from that class or from
other classes. Similar considerations apply to protected methods.
A further variation on class definitions consists in changing the type of some pre-
method Ij of Class(A) from A~Bj to C~Bj for some C such that A <: C. While pre-meth-
ods of Class(C) can normally be inherited into Class(A), this change allows a pre-
method Ij of the modified Class(A) to be inherited "backwards" into Class( C). This form
of inheritance can sometimes be useful, but does not seem to lend itself to a systematic
treatment.

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

In our notation, PrivateCellClass is simply Class(PrivateCell). A typical element of this


class type is:
privateCellClass : PrivateCellCiass £
[new =
r;(z:PrivateCellClass)
[contents = r;(s:PrivateCell) z.contents(s),
get = r;(s:PrivateCell) z.get(s),
set = r;(s:PrivateCell) z.set(s) I,
contents = A(s:PrivateCell) 0,
get = A(s:PrivateCell) s.contents,
set = A(s:PrivateCell) A.(n:Nat) s.contents := nl
By subsumption, privateCellClass can be seen as a trait, from which other classes
can inherit pre-methods:
104 PART I. UNTYPED AND FIRsr-ORDER CALCULI

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 Objects versus Records


In this section we compare object types with record types. We consider two natural but
incorrect extensions of Ob1<:: method extraction and covariant object types. Taken
together, these two failed extensions show that, in a calculus with subsumption, object
typing is fundamentally different from record typing because both extensions are
sound for records. Another extension, elder, allows us to define a new version of a
method in terms of the previous one.

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

(Type Record) (Ii distinct)


E I- Bi 'v'iEl .. n
E I- (li:Bi iE l..n)

(Sub Record) (Ii distinct)


EI-Bi<: B/ 'v'iE1..n EI-Bi \;fiEn+1..n+m
E I- (li:Bi iEl..n+m) <: (li:B/ iEl..n)

(Val Record) (Val Record Select)


E I- bi : Bi 'v'iE1 .. n E I- a: (Ii:Bi iE1..n) jE1..n
E I- a.Ij : Bj

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 Sub Record)


E I- bj : Bi \;;fiEl..n+m

(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

Functional record update is definable as follows:


a.lj:=b ! (Ij=b, li=a.l i iE(!..nHjl)

Derived rules for this operation are:


(Val Record Update)
E I- a: (/i:Bi iE1 ..n) E I- b: B jEl..n
E I- a.lr=b : (/j:B, li:Bi ie(1..n)-UI)

(Eq Record Update)


E I- a H a': (li: Bi iE1..n) E I- b H b' : B jEl ..n
E I- a.lj:=b H a' .lj:=b': (lrB, li:Bi iE(1..n)-UI)

(Eval Record Update) {where a'" (/i=bi i'I.."»


E I-a : (li:Bi iE!..") E I- b: B jEl..n

8.6.2 Method Extraction


Let us assume we have an operation for extracting a method from an object, as dis-
cussed at the beginning of Chapter 8. Considering the analogous rules for records, it
would seem natural to give the following rules:
(Val Extract) (where A '" [li: Bi iEI.."J)
E I-a: A jEl..n
E I- a .l j : A~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

The extraction operation is unsound, as the following example shows:


P ! Ix:Int,fIntl
p ! Ix=l,f=l) p:P by (Val Object)
Q ! Ix,y:lnt, f:Int) Q <: P by (Sub Object)
a ! Ix=l, y=l,f=<;(s:Q)s .x+s.y) a: Q by (Val Object), hence also
a:P by (Val Subsumption)
a-f b : P~Int by (Val Extract),
with b H A(S:P)S.x+s.y :P~Int
by (Eval Extract)
Now we havej1l f- b(p) H (A(S:P)S.x+s.y)(p) H p.x+p.y : Int, via (Eval Beta), butp does not
have a y component. If we change an A to A' in the conclusion of (Eval Extract), which
becomes E f- a.lj H A(xj:A')bj : A~Bj, then the (Eval Beta) step is not even possible.
Hence it is unsound to add the method extraction operation to FObl <: (or to Obl <"
with encoded function types), although it is sound to add it to FObl (or to ObI).

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).

8.6.4 Covariant Object Types


The subtyping rule for object types should be compared with the analogous rule for
product types. Object types are invariant in their components, whereas product types
are covariant in both their components. Similarly, record types (li:Ai i£1..n), which gen-
eralize product types, are covariant in all their components.
It is natural to ask what happens if we allow object types to be covariant in their
components. Suppose we adopt the following more liberal subtyping rule for objects:
(Sub Object/ covariant) (Ii distinct)
E I- Bi < : B;' E I- Bj V'iEl..n, jEn+ l..m
E I- [li:Bi iEl..n+m] < : [li: B;' i£1..n]

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.

8.7 Variance Annotations


Our basic object types are invariant in their components. In this section we study the
possibility of building variance into object types. The techniques developed in this sec-
110 PART I. UNTYPED AND FIRST-ORDER CALCULI

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.

8.7.1 Object Types with Variance


Variance properties are expressed by annotating each component of an object type as
invariant, covariant, or contravariant. Invariant components are the familiar ones.
Covariant components allow covariant subtyping, but prevent updating. Contravari-
ant components allow contravariant subtyping, but prevent invocation. Invariant com-
ponents can be regarded, by subtyping, as either covariant or contravariant.
Variance annotations are attractive, from the points of view of both programming
and theory. From a programming perspective, variance annotations support flexible
subtyping, along with protection from unwanted reading or writing. From a theoreti-
cal perspective, they model a range of realistic features in an orthogonal and relatively
simple way. Variance annotations can be encoded using quantifiers, but the encoding
is not simple (see Chapter 18).
In this section we describe an extension of Ob1 <: with variance annotations. Object
types now have the form [li'Ui:Bi iEJ..n] where each 'Ui (a variance annotation) is one of the
symbols -, 0, and +, for contravariance, invariance, and covariance, respectively.
Object types with variance annotations
I
(Type Object) (Ii distinct, "i E (O,-:})
E I- Bi 'v'iEl..n
E I- [li'Ui:Bi iEJ..n]

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

(Sub Invariant) (Sub Covariant) (Sub Contravariant)


EI-B E I- B <: B' 'UE {o,+) E I- B' <: B 'UE {O,-)
E I- °B <: °B E I- 'U B <: + B' E I- V B <: -B'
8. SUBTYPING 111

These four rules say:


• (Sub Object) Components that occur both on the left and on the right are han-
dled by the other three rules. For components that occur only on the left, the
component types must be well-formed.
• (Sub Invariant) An invariant component on the right requires an identical one
on the left.
• (Sub Covariant) A covariant component type on the right can be a supertype of
a corresponding component type on the left, either covariant or invariant. Intu-
itively, an invariant component can be regarded as covariant.
• (Sub Contravariant) A contravariant component type on the right can be a sub-
type of a corresponding component type on the left, either contravariant or
invariant. Intuitively, an invariant component can be regarded as contravariant.
For example, these rules imply that if B <: B' then [1°:B'] <: [r:B] and (lD:B] < : W:B'], but
[r:B] and W:B'] are unrelated.
To preserve soundness in the presence of variant components, the rules for selec-
tion and update are restricted: selection cannot operate on contravariant components
and update cannot operate on covariant components:
Typing with variance annotations
I
(Val Object) (where A == [I,v;:B; ;01.."))
E, Xi:A I- bi : Bi ViE l..n
E I- [li=~(Xi:A)bi iol..n] : A

(Val Select)
E f- a : [1i\li:Bi iel..n] \ljE (O, +) jE l..n
E I- a.lj: Bj

(Val Update) (where A == [I,v;:B; ;<1.."))


Ef-a:A E,x:Al-b:Bj \ljE{O,-) jEl..n
E I- a.lj:.=~(x:A)b : A

The rule (Val Object) is essentially unchanged since we add variance annotations only
to object types, not to objects.

8.7.2 Encodings and Examples with Variance


We show the expressiveness of variance annotations by representing variant type oper-
ators as object types with variance annotations.
We begin with covariant product types:
112 PART I. UNTYPED AND FIRST-ORDER CALCULI

B1x+ B2 g, [lft+:B 1, rht+:B 2 ]


(b 1,b2 ) g, [lft=<;(x:B1x+B2)b1, rht=<;(x:B1x+ B2 )b2]
We can show that if bl : BI and b2 : B2- then (b 1,b2 ) : B1x+B2- with a dummy assumption
x: B1x+B2 for the self variables.
The encoding of records works similarly:
(li:Bi ifl..n)+ g, [It:Bi ifl..n]
(li=b i ifl..n) g, [li=<;(xi:(li:Bi ifl..n)+)bi ifl..n] for xitFV(b i) and bi : Bi
a.lj g, a.lj for a: (li:Bi i<l..n)+, jEl..n
The rules for records given in Section 8.6.1 are validated by this encoding. Thus cova-
riance annotations unify object types and record types.
As shown in Section 7.7, functions can be expressed in Ob1 and hence in Ob1 <:. A
function A(x:A)b with argument type A and result type B receives the type [argo:A,
valo:B]. This typing, however, is invariant in both A and B. With variance annotations,
we can define a type construction A~-+B, contravariant in A and covariant in B:
A~-+B g, [arg-:A, val+:B]

obtaining [argo:A, valo:B] <: A~-+B


The encoding of abstractions and applications is unchanged:
A(x:A)b{x) g,
[arg=<;(x:[argo:A, valo:B])x.arg, val=<;(x:[argo:A, valo:B])b{n-x.arg)]
b(a) g, (b.arg:=a).val
We have that A(x:A)b{x) has type [argo:A, valo:B], as before, and hence has also type
A~-+B, by subsumption. Application still typechecks: if b: A~-+B and a : A, then in b(a)
the assignment to arg is legal because of the contravariance of arg, and the invocation
of val is legal because of the covariance of val.
This encoding illustrates the use of an intermediate type with invariant compo-
nents as a subtype of a desired type with variant components. As in this encoding, we
can often begin by defining objects with invariant components that can be internally
read and written through self. Then, by subsumption, certain components can be pro-
tected against external reads or writes, obtaining covariance and contravariance with-
out preventing internal reads and writes. In the encoding of A-terms, x.arg can be read
internally, although externally it can only be written.
As a further example, we decorate the cell types of Section 8.2 with variance anno-
tations. We define the types:
ProtectedRomCell g, [get+:Nat]
ProtectedPromCell g, [get+ :Nat, set+:Nat~ProtectedRomCell]
ProtectedPrivateCell g, [contents:Nat, get+:Nat, set+:Nat~ProtectedRomCell]
The + annotations protect the methods get and set from updates from the outside. We
obtain the subtypings ProtectedPrivateCell <: ProtectedPromCell <: ProtectedRomCell.
9 RECURSION

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

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
114 PART I. UNTYPED AND FIRST-ORDER CALCULI

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

We obtain the calculi:


ObI!! £ ObI U !!.X U !!.!! ObI with recursion
FI!! £ FI U!!.xU!!.!! FI with recursion
FObI!! £ FObIu!!.x U !!.!! FOb I with recursion
with equations:

(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'"

9.2 Recursion and Subsumption


To add recursion to a calculus with subtyping we cannot reuse directly the fragments
l'1x and l'1" of Section 9.1, because type variables in environments now require subtype
bounds. Therefore we introduce new fragments l'1<:x and l'1 <:1'" In l'1<:x the components
of environments are type variables with bounds, of the form X<:A. The fragment l'1<:"
includes a rule for sub typing recursive types, (Sub Rec), which uses the bounded type
variables. We may still write E, X, E', but now as an abbreviation for E, X< :Top, E'.
116 PART 1. UNTYPED AND FIRSf -ORDER CALCULI

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.

(Type Ree<:) (Sub Ree)


E, X<:Top f- A E f- ~(X)A E f- ~(Y)B E, Y<:Top, X<:YI- A <: B
E f- ~(X)A E f- ~(X)A <: ~(Y)B

(Val Fold) (where A == I1(X)B\X)) (Val Unfold) (where A == I1(X)B\X))


E f- b: BHAB El-a:A
E f- fold(A,b) : A E f- unfold(a) : BHAB

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

a,b ::= terms


X variable
[lj=C;(xj:Aj)bj jt1..nj object (Ij distinct)
a.l method invocation
a.l~c;(x:A)b method update
A(x:A)b function
b(a) application
fold(A,a) recursive fold
unfold(a) recursive unfold

Scoping for the FOb 1<:" calculus


FV(X) ! IX}
FV(K) ! II
FV(Top) ! II
FV([lj:Bj jt1.. n n ! u jt 1..n FV(Bj)
FV(A-?B) ! FV(A) u FV(B)
FV(Il(X)A) ! FV(A)-(X}
FV(C;(x:A)b) ! FV(A) u (FV(b)-(x))
FV(x) ! Ix}
FV([lj=C;(xj:Aj)bj jt1..nj) ! u j<1..n FV(C;(xj:Aj)bj)
FV(a.l) ! FV(a)
FV(a.l~c;(x:A)b) ! FV(a) u FV(C;(x:A)b)
FV(A(X:A)b) ! FV(A) u (FV(b)-(x))
FV(b(a)) ! FV(b) u FV(a)
FV(fold(A,a» ! FV(A) u FV(a)
FV(unfold(a)) ! FV(a)

9.3 Some Properties of Ob1<:J.l


In this section, we extend the basic results about Ob 1<: to Ob1 <:". As usual, we expect
analogous properties to hold for FOb1<:".

9.3.1 Minimum Types


We prove the minimum-types property for a restricted version rOb 1<:" of Ob 1<:w In this
version, the rules (Env x) and (Val Subsumption) are applicable only for closed types,
that is, we replace (Env x) and (Val Subsumption) with the weaker rules:
9. RECURSION 119

Restricted rules for rOb1<:Il


I
(Env x Restricted)
E f- <> j1I f- A x;'dom(E)
E, x:A f- <>

(Val Subsumption Restricted)


E f- a : A j1I f- A <: B

Ef-a:B

The restriction implies that if E f- a : A is derivable then A is closed. However, it


does not affect the typings obtainable in an empty environment. The restriction simpli-
fies our argument; a technique for handling the general case is described in [56) in the
context of a different calculus.
In order to prove the minimum-types property for rOb 1<:", we consider a system
rMinOb1<:11 obtained from rOb 1<:11 by removing (Val Subsumption), by modifying (Val
Object) and (Val Update) as in MinOb1<: from Section 8.3.1, and by modifying (Val
Fold) as follows:

Modified rule for fold for rMinOb1<:Il


(Val Min Fold) (where A'" I1(X)B/X))
E f- b : C j1I f- C <: BiAB

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

Proposition 9.3-4 (rOb1<:1! has minimum types)


In rOb 1<:fl/ if E f- a : A then there exists B such that E f- a : B and, for any A ', if E f- a :
A ' then E f- B <: A'.
D
From this we obtain a restricted result for the original system Ob1<:I!.
Corollary 9.3-5 (Ob1<:1! has minimum types in an empty environment)
In Ob1<:fl/ if ~ f- a: A then there exists B such that ~ f- a: B and, for any A', if ~ f- a:
A' then ~ f- B <: A'.
D

9.3.2 Subject Reduction


We extend the operational semantics to treat fold and unfold. The set of results now
includes the terms of the formfold(A,v), where v is a result.
Operational semantics for fold and unfold
(Red Fold)
f-a --v
f- fold(A,a) -- fo1d(A,v)

(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)

Lemma 9.3-6 (Bound weakening)


If E, X<:D, E' f- Sand E f- D' <: D, then E, X<:D ', E' f- S .
If E, x:D, E' f- Sand E f- D' <: D, then E, x:D', E' f- S.
D
9. RECURSION 121

Lemma 9.3-7 (Substitution)


If E, X<:A, E'{X} f- g\X} and E f- A' <: A, then E, E'iA'j I- giA'j.
If E, x:D, E' f- g{x} and E I- d : D, then E, E' f- g{d).
o
The proof of subject reduction is then routine.
Theorem 9.3-8 (Subject reduction for Obl<:~
Let c be a closed term and v be a result, and assume I- c --- v.
If Jill- c : C, then Jill- v : C.
Proof
We add two cases to the proof of Theorem 8.3-7, which is by induction on the der-
ivation of f- c v+ v.
Case (Red Fold)
Suppose I- fo1d(A,a) --- fold(A,v) because I- a v+ v. Assume that Jill- fo1d(A,a) : C. Then
A has the form Il(X)B{X}, Jill- A <: C, and Jill- a : BIA». By induction hypothesis, we
have Jill- v : B!Aj. Therefore we obtain Jill- fo1d(A,v) : BHAa by (Val Fold), and Jill-
fo1d(A,v) : C by subsumption.
Case (Red Unfold)
Suppose I- unfold(a) --- v because I-a v+ fold(A,v) . Assume that Jill- unfold(a): C. Then
Jill- a : Il(X)B{X} for some type of the form Il(X)B{X} such that Jill- Bill(X)B! <: C. By
induction hypothesis, we have Jill- fo1d(A,v) : Il(X)B. This implies that A has the
form Il(X')B'{X'J, that Jill- Il(X')B' <: Il(X)B, and that JIl f- v : B'!Il(X')B'j. Since Jill-
Il(X' )B' <: Il(X)B, either Il(X')B' is Il(X)B or JIl, X<:Top, X'<:X I- B' <: B. In the former
case, Jill- v: B{Il(X)B»' In the latter case, by Lemma 9.3-7, Jill- B'ill(X')B'} <: B!Il(X)BH,
and we obtain Jill- v : Bill(X)Bj again, by subsumption. In either case it follows that
Jill- v : C by subsumption.
o

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

backup = ~(s2:UBk)jold(Bk, s2.retrieve ~ ~(sl:UBk)jold(Bk, S2»,

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

iszero ~ t..(n:Nat) ifiunfold(n).case)(t..(u:Unit) true)(t..(p:Nat) false)


pred ~ t..(n:Nat) ifiunfold(n).case)(t..(u:Unit) zero)(t..(p:Nat) p)
Although this code looks quite different from that of Section 6.5.3, the two versions are
related by a type isomorphism (explained in Section 15.2.4).

9.5 The Shortcomings of First-Order Typing


The FOb1<:/L calculus looks very promising because it adds subtyping to a rich first-
order theory. For example, we can use it to write types of movable points, as we did in
Section 9.4:
PI ~ ~(X)[x:lnt, mv_x:lnt-7X] movable one-dimensional points
P2 ~ ~(X)[x,y:lnt, mv_x,mv--y:lnt-7X] movable two-dimensional points
In addition, since we have subtyping, we may expect to obtain that P 2 <: PI, because
intuitively P 2 extends Pl. However, this inclusion is not provable with our rules,
because the invariance of object types blocks the application of (Sub Rec) to the result
type of mv_x. In fact, the inclusion P2 <: PI is unsound, as we show next.
We derive an inconsistency from P 2 <: Pl. Briefly, if we assume P 2 < : PI, we can use
subsumption from p : P2 to p: PI and then update the mv_x method of p with one that
returns a proper element of Pl. Then some other method of p may go wrong because it
expects mv_x to produce an element of P2 . More precisely, we can construct the follow-
ing counterexample:
UP I ~ [x:lnt, mv_x:lnt--7PI ] (the unfolding of PI)
UP 2 ~ [x,y:lnt, mv_x,mv--y:lnt--7P2] (the unfolding of P2)
P2: P2 ~
fold(h
[x = C;(S2:UP2) unfold(s2.mv_x(1».y,
y=O,
mv_x = C;(52:UP2) t..(dx:lnt)fold(P2f 52),
mv--y = C;(52:UP2) A(dy:lnt) fold(P2f 52)])
PI : PI ~ fold(h [x = 0, mv_x = C;(51:UPI) t..(dx:lnt) fold(h 51)])
P : PI ~ P2 (retyping P2 using the assumption P2 <: PI)
q : PI ~ fold(h unfold(p).mv_x:= t..(dx:lnt) pd
We have:
unfold(q).x
= (unfold(P2).mv_x:= t..(dx:lnt) PI).X
124 PART I. UNTYPED AND FIRST-ORDER CALCULI

[x = ~(S2:UP2) unfold(s2.mv_x(1)).y, mv_x = A(dx:lnt) PI,


y= ... ,mv....Y= ... ].x
unfold(PI).y
But unfold(PI) does not have a y component.
As we have just seen, the failure of P2 <: PI is necessary. At the same time, it is unac-
ceptable: in the common situation where a method returns an updated self, we lose all
useful subsumption relations.
In many programming languages, these difficulties are avoided by not allowing a
subclass to change the type of a method of a superclass. In our calculus, similarly, these
difficulties are avoided if we define mv_x to return PI even when embedded in P2:
PI !, Il(X)[x:lnt, mv_x : lnt~X]
P2t !, Il(X)[x,y:Int, mv_x:lnt~PI' mv....Y:lnt~X]
UP I !, [x:lnt, mv_x:lnt~PI] (the unfolding of PI)
UP2 t £ [x,y:lnt, mv_x:Int~PI' mv....Y:lnt~P2t] (the unfolding of P2t)
Then we have Up 2 <: UP!, so we can at least convert every point P : p 2t to a point
t
fold(hunfold(p)) of type h It is possible to strengthen the type theory to identify recur-
sive types with their unfoldings, as in (18); then we can obtain directly p 2t <: PI.
After these changes, the pre-method mv_x can be inherited because its result type
remains the same. However, whenever we invoke mv_x on an element of p 2t , we "for-
get" its second dimension. For this kind of solution to be useful, Simula and Modula-
3, among other languages, provide dynamic testing of membership in a subtype of a
given type, so that the forgotten information can be recovered (see Section 2.5). In our
example we would test for membership in the subtype P2t of PI. We would abandon,
though, the truly static typing of subsumption. We explore this approach in Section 9.7.
Another possible solution is based on the variance annotations of Section 8.7.
Using variance annotations, we can rewrite:
Pl v !, Il(X)[x:lnt, mv_x+:lnt~X]
P{ £ Il(X)[x,y:lnt, mv_x+,mv....Y+:lnt~X]
The subtyping P/ <: P{ holds because of the covariance of mv _x. The counterexample
above is avoided because mv_x can no longer be updated.
This solution might seem sufficient at least for class-based languages, where meth-
ods cannot be updated. Unfortunately, subtyping problems resurface in the treatment
of classes and inheritance. Assume, for simplicity, the equivalence of Pl v and P2v with
their respective unfoldings, and consider the class types associated with Pl v and P/
(see Section 8.5):
Class(PIV) !, [new:P l v, mv_x:P{~lnt~Plv, ... ]
Class (P{) £ [new:P{, mv_x:P{~lnt~P2v, ... ]
Now, Plv~lnt~Plv <: P2v~lnt~P2v fails because of the result types. Hence the pre-
method mv_x cannot be inherited from a class CI of type Class(P IV) to a subclass C2 of
9. RECURSION 125

type Class(P2V). Such an inheritance would be unsound in general, because mv_x in C2


must return a P{, while mv_x in CI may return a PIv that is not a P2 v .
This conclusion is unacceptable as well: many methods have the type of self as
their result type, and many of these methods can be soundly inherited (for example, if
they simply return self). A task of a good type system should be to distinguish the
sound instances of inheritance from the unsound ones. At the very least, we need a
more sophisticated treatment of classes.
All these problems are less severe in imperative languages, where the mv_x
method could be redefined to cause a side-effect on the host point and return nothing.
Then the type of the modified method would not depend on the type of self, and PI and
P 2 would not be recursive (see Section 11.2.1). Even in imperative languages, though,
we often find methods that allocate new objects of the type of self and return them; it
is not reasonable to forbid recursive types and to require the use of side-effects in situ-
ations where side-effects are not natural.

9.6 Towards the Type Self


In Part II, we look for a satisfactory treatment of self-returning methods. A natural idea
in this direction is to handle the type of self in some special way. A proposal introduces
a distinguished type as the type of self. This type, which we call Self, denotes in every
subtype of an object type, the subtype in question (see Section 2.8). Intuitively, Self is
the partially unknown type of the self parameter of each method. Using Self, we would
wish to write the types:
PI' ~ [x:lnt, mv_x:lnt-7Selj]
P2' ~ [x,y:lnt, mv_x,mv~ : Int-7Selj]
with the following desirable properties:
P2'<:PI '
if PI': PI ' and P2' : P2', then PI '. mv_x: Int-7PI' and P2'.mv_x: Int-7P2'
Several object-oriented languages have included Self in their type systems [88, 116}.
Therefore it seems important to have a precise understanding of Self. The counterex-
ample to P 2 <: PI given in Section 9.5 still looms. In fact, Eiffel has soundness problems
because of such counterexamples [55}; these problems are addressed only outside the
typing discipline.
Still, it may be possible to use the type Self, instead of recursive types, and to adopt
ad hoc typing rules for Self that avoid unsoundness. An appealing idea is to require
that any term meant to update a method returning Self should be parametric in Self: that
is, it should always return a modification of its self parameter, and never return a fixed
object [61}. This requirement blocks the counterexample in Section 9.5, while allowing
the definition of useful movable points.
The tentative conclusion we should draw here is that recursive types are the wrong
way of modeling the type Self (whatever this is). We could now proceed to extend
126 PART I. UNTYPED AND FIRSf -ORDER CALCULI

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 Dynamic Typing


As an alternative to extending our calculi with the type Self, we could add dynamic
typing to FOb1<:11" This addition would preserve the first-order character of the calcu-
lus, and enable many realistic examples; it is methodologically problematic, as we dis-
cussed in the Review, but it is technically simple.
In this section, we describe the technical aspects of adding a particular typecase con-
struct to FObl<:~ other forms of typecase are possible [8J.

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

discrimination. An operational semantics for an untyped calculus, such as the one of


Section 6.2.4, could be augmented with tagged results in order to handle typecase [8).
We adopt an operational semantics without such tags; in our semantics, typecase
performs run-time type discrimination by constructing a typing derivation. Our rules
do not immediately suggest an efficient implementation, but have the advantage of
being simple and general, applying equally well to calculi with and without subtyping.

Operational semantics for typecase


r
(Red Typecase Match)
r- a ..,... v' fI! r- v' : A r- d1tv'» ..,... v
r- typecase a I (X:A)dl {x} I d2 ..,... V
(Red Typecase Else)
r- a ..,... v' fI! If v' : A r- d2 -- v
r- typecase a I (x:A)d 1 I d2 ..,... V
These rules are unusual in that the result of a reduction depends on whether a typing
is derivable. If fI! r- v' : A is derivable, then the first rule is applicable. Otherwise (when
fI! If v' : A), the second rule is applicable; thus the second rule has a negative assumption.

9.7.2 Subject Reduction


Although typecase permits dynamic typing, the static typing rules remain consistent. In
fact, it is straightforward to extend our subject reduction results to typecase.
Lemma 9.7-1 (Bound weakening)
If E, X<:D, E' r- g and E r- D' <: D, then E, X<:D', E' r- g.
If E, x:D, E' r- g and E r- D' <: D, then E, x:D', E' r- g.
o
Lemma 9.7-2 (Substitution)
If E, X<:A, E'{X} r- g{X} and E r- A' < : A, then E, E'(A'j r- g{A'j.
If E, x:D, E' r- g{x} and E r- d : D, then E, E' r- g{dl
o
Theorem 9.7-3 (Subject reduction for Ob1<:j! with typecase)
Let c be a closed term and v be a result, and assume r- c ..,... v.
If fI! r- c :C, then fI! r- v : C.
Proof
We add two cases to the previous proof of subject reduction (Theorem 9.3-8),
which is by induction on the derivation of r- c -- v.
128 PART 1. UNTYPED AND FIRST-ORDER CALCULI

Case (Red Typecase Match)


Suppose f- typecase a I(x:A)d1 {xII d2 -- v because f- a -- v', fII f- v' : A, and f- dl(v'. --
v. Assume that fill-- typecase a I (x:A)d l {x} Id2: C. Then fII, x:A f- dl {xl: D for some type
D such that fII f- D <: C. By Lemmas 9.7-1 and 9.7-2 we obtain fill-- dl(v'. : D. We
obtain fII f- v : D by induction hypothesis, and fII f- v : C by subsumption.
Case (Red Typecase Else)
Suppose I-- typecase a I(x:A)d1 Id2 -- v because f- a -- v', fIIlf v' : A, and f- d2 -- v.
Assume that fill-- typecase a I(x:A)dl Id2: C. Then fII f- d2 : D for some type D such that
fII f- D <: C. We obtain fII f- v : D by induction hypothesis, and fII f- v : C by subsump-
tion. (Note that the premise fIIlf v' : A is not used in this case.)
o
9.7.3 An Example
Using typecase, we can recover lost type information. For example, in Section 9.5 we
introduced the following types for movable points:
PI ! IJ.(X)[x:lnt, mv_x:lnt~X]
p 2t ! IJ.(X)[x,y:lnt, mv_x:lnt~h mv-y:lnt~X]
UP I ! [x :lnt, mv_x:lnt~PI] (the unfolding of PI)
UP2t ! [x,y:lnt, mv_x:lnt~PI' mv-y:lnt~P2t] (the unfolding of P2t)
with the inclusion UP2t <: UPI . Consider a two-dimensional point p:
p : P2 t !
fold(P2 t ,
[x = 0, y= 0,
mv_x = r;(S2:UP2t) "A.(dx:lnt) fold(P I , S
2.X := s2.x+dx),
mv-y = r;(S2:UP2t) "A.(dy:lnt)fo1d(P2 t , s2-Y := S2-Y+dy)J)
We have:
p': PI ! unfold(p).mv_x(l)
unfold(p') =
[x = 1, Y = 0,
mv_x = r;(S2:UP/) A(dx:lnt) fo1d(PI, S2.X ;= S2.x+dx),
mv-y = r;(S2:UP/) "A.(dy;Int)fo1d(P2t , s2-Y:= S2-Y+dy)]
Therefore, although p' has static type PI, the result of unfold(p ') is a two-dimensional
point with type UP2t . Using typecase, we can recover the forgotten y component of p';
typecase unfold(p') I (Up':UP2t) up'.y I -1 = 0
10 UNTYPED IMPERATIVE CALCULI

Object-oriented languages are naturally imperative, with methods performing side-


effects on the internal state of objects. So far we have examined only stateless execution
models. In the next two chapters we show that our theory of objects is applicable to
imperative languages, by adapting our techniques to an operational semantics with
side-effects.
In this chapter we develop a tiny but expressive untyped imperative calculus, an
imperative variant of the calculus of Chapter 6. This imperative calculus forms the ker-
nel of the Obliq programming language. The calculus comprises objects, method invo-
cation, method update, object cloning, and local definitions. Although an object is
usually seen as a bundle of mutable fields together with a method suite, in our calculus
the method suite is mutable, so we can dispense with the fields.
We start with the syntax of a bare calculus. Then we detail a few encodings, and
give some illustrative programming examples. In particular, we show how to express
convenient constructs such as fields, procedures, data structures, and control struc-
tures. Finally, we formalize the operational semantics of the calculus.

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

As in Chapter 6, an object [lj=C;(xj)b j jEl..n] is a collection of components Ij=C;(xj)bj,


where Ij is a method name and C;(xj)bj is a method. The binders <;(Xj) suspend the evalu-
ation of the bodies bj, hence the order of the components Ij=C;(xj)bj does not matter.
Method invocation is still based on the self-application idea: if the method named I in
a is C;(x)b, then a./ executes the body b with x bound to a. Method update, a.I~<;(y)b, is

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
130 PART I. UNTYPED AND FIRST-ORDER CALCULI

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:

Translation of imp9 into imp~


«x» ~ x
«[/i=b i iEI .. n, Ij=~(xj)bj jEn+1..n+m]» ~ for Yi~FV(bk kE1..n+m), Yi distinct, iED .. n
let YI=«b l » in .. . let Yn=«b n» in [li=~(Yo)yi iEl..n, Ij=~(xj)«bj» jEn+l..n+m]
«a.l» ~ M .I
«a.I:=b» ~ for Yi~FV(b), Yi distinct, iED.. 2
let YI=«a» in let Y2=«b» in Yl.lf;=~(YO)Y2
«a.lf;=~(x)b» ~ «a».I~~(x)«b»

«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

In the translation, an environment p maps each variable x either to x.arg if x is A-


bound, or to x if x is a free variable. As in Section 6.3, a A-abstraction is translated to an
object with an arg component, for storing the argument, and a val method, for executing
the body. The arg component is initially set to a divergent method, and is filled with an
argument upon procedure call. A call activates the val method that can then access the
argument through self as x.arg. An assignment x:=a updates x.arg, where the argument
is stored (assuming that x is A-bound).
A procedure needs to be cloned when it is called; the clone provides a fresh loca-
tion in which to store the argument of the call, preventing interference with other calls
of the same procedure. Such interference would derail recursive invocations.
This encoding has similarities with the mechanism of method activation in Self
and Beta, and with a brief comment of Adams and Rees in [11) . It is unrelated to the
encoding of functions with objects in Oaklisp [76).
We can extend the impA-calculus into a calculus with procedures and objects,
impAc;t. This extended calculus has procedures with call-by-value parameters and
assignable formals, objects with fields and methods, constant declarations (let), vari-
able declarations (var), and sequencing (;). Sequencing and let are defined as abbrevia-
tions as in impc;/. The syntax of impAc;t is simply the union of the syntaxes of impA and
impC;f.
10. UNTYPED IMPERATIVE CALCUU 133

Syntax of the impAc;r-calculus


a,b ::= terms
x variable
[li=bi iELn, 1,=C;(xj)bj jEn+l..n+m] object (Ii, Ij distinct)
a.l field selection / method invocation
a.l:=b field update
a.l:=c;(x)b method update
clone(a) cloning
x:=a assignment
A(x)b abstraction
b(a) application
(abbreviations)
let x=a in b local identifier
var x=a in b local variable
a; b sequencing

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.2 Object-Oriented Natural Numbers


Our imperative numerals have a case field and a suee method. The case field of a
numeral takes two procedures (that is, case branches) as parameters. The first branch
is invoked in the zero case with a dummy argument (to delay evaluation). The second
branch is invoked in the non-zero case, with the predecessor of the current numeral as
argument. The succ method of a numeral produces its successor.
Therefore the case field for zero invokes its first argument. The suee method must
convert any numeral into a non-zero numeral. First the current numeral (available as
self) is cloned. Then the ease field of the clone is modified to invoke the second branch
with self as parameter. Finally the modified clone is returned.
let zero =
[ease = A.(z) A.(s) z([)),
suee = r;(x) clone(x).ease:= A.(z) A.(s) s(x»);
Cloning protects a numeral from being permanently modified when its successor
method is invoked . As an exercise, the reader may try to express the natural numbers
without using cloning, via recursive definitions.
When zero.suee is executed, a clone of the numeral zero is transformed into the
numeral one. In other words, zero is used as a prototype for one. Of the components of
zero, the succ method is inherited, while the case field is updated. This transformation
is an example of cloning plus update, typical of prototype-based languages.
A convenient control structure for primitive recursion over the natural numbers
can be defined as follows:
ease n when 0 do e, when m+ 1 do d ~
(n.ease) (A.(w)e) (A.(m)d) for w~FV(e)

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.

10.4.4 A Prime Number Sieve


Our final example is an implementation of the prime number sieve. This example is
meant to illustrate advanced usage of object-oriented features, and not necessarily
transparent programming style.
let sieve =
[m = ~(s) A(n)
let sieve' = cIone(s)
in s.prime := n;
s.next := sieve';
s.m ~ ~(s') A(n')
case (n' mod n)
when Odo [J,
when p+ 1 do sieve'.m(n');
[J,
prime = ~(x) x.prime,
next = ~(x) x.next];
The sieve starts as a root object which, whenever it receives a prime p, splits itself
into a filter for multiples of p, and a clone of itself. As filters accumulate in a pipeline,
they prevent multiples of known primes from reaching the root object. After the inte-
gers from 2 to n have been fed to the sieve, there are as many filter objects as there are
primes smaller than or equal to n, plus a root object. Each prime is stored in its filter;
the n-th prime can be recovered by scanning the pipeline for the n-th filter.
The sieve is used, for example, in the following way:
for i in 1..99 do sieve.m(i.succ); (accumulate the primes ~ 100)
sieve.next.next.prime (returns the third prime)

10.5 Operational Semantics


The semantics of our imperative calculi is based exclusively on reduction relations; we
do not define equational theories.
136 PART I. UNTYPED AND FIRST-ORDER CALCULI

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.

10.5.1 Reduction Rules


We now list the entities used in the operational semantics, the judgments that relate
them to terms, and the reduction rules that manipulate them. We represent stacks and
stores as finite sequences. In particular, the sequence lj-mj jEl ..11 is the store that maps
the location lj to the closure mj, for iE1..n. We let o.lj.... m denote the result of storing m
in the location lj of 0, so if 0 ;;; lj-mj i.l..n and jE l..n then o .lj....m ;;; lj-m, li-mi iEI .. n-lil.
Operational semantics
l store location (e.g., an integer)
V ::= [Ii=li iEl..n) result (Ii distinct)
o ::= li-(~(Xi)bi,5i) i.l..n store (li distinct)
5 ::= Xi ....Vi i.l.." stack (Xi distinct)
01-0 well-formed store judgment
0.5 I- 0 well-formed stack judgment
0.5 I- a ..... v.o' term reduction judgment

(Store j1J) (Store L)


0.5 I- 0 l~dom(o)

1111-0 0, l-(~(x)b,5) I- 0

(Stack j1J) (Stack x) (Ii, li distinct)


01-0 0.5 I- 0 x~dom(5) \;fiEl..n
0.1111- 0 0.(5, x .... [li=li iEI..,,)) I- 0
10. UNTYPED IMPERATNE CALCULI 137

(Red x)
a.(5', x ....v, 5") I- 0

a.(5', X....v, 5") I- x -- v·a

(Red Object) (Ii, Li distinct)


a.5 I- 0 Li~dom(a) 'v'iE1 .. n

(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 Clone) (Li'distinct)


0.5 I- a -- [lFLi iE1..nj.a' LiEdom(a') L/~dom(a') 'v'iE1..n
a.5 I- clone(a) -- [/i=L/ iE1.."j.(O', L;'I-+a'(Li) iE!..")

(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

10.5.2 Examples of Reductions


To illustrate the behavior of imperative terms, we detail some reductions using the
notation of Section 7.1.
The first example is a simple terminating reduction.
!1S·!1S r [l=~(x)[]] -- [1=O].(O ...... (~(x)[],!1S» by (Red Object)
f
(O ...... (~(x)[],!1S».(x ...... [/=O]) r [] - [].(O ...... (~(x)[],!1S» by (Red Object)
!1S,!1S r [l=<;(x)[]].l- [].(O ...... (<;(x)[],!1S» (Red Select)
The next one is a divergent reduction; we show the incomplete derivation tree that
is built in the attempt to obtain a judgment of the form !1S'!1S r [l=<;(x)x.l].l--?? The tree
displays a repeating pattern on an infinite branch.
!1S'!1S r [l=<;(x)x.l] -- [l=O].(O ......(<;(x)x.l,!1S» by (Red Object)
(~~(~(X)X.l,!1S».(X ...... [I=OD r x -- [1=O].(O ......(<;(x)x.l,!1S» by (Red x)
[
r
(O ..... (<;(x)x.l,!1S».(x ...... (I=OD r x.l-- ?? by (Red Select)
(O ......(<;(x)x.l,!1S».(x ...... [I=OD r x.l- ?? (Red Select)
!1S.!1S r (l=~(x)x.l].l-- ?? (Red Select)
As a variation of this example, we can have a divergent reduction that keeps allo-
cating storage. Read from the bottom up, the tree for this reduction displays judgments
with increasingly large stores, 00, 01, . . . :
00 ! O.....(<;(x )clone(x ).1, !1S)
01 ! 00, 1...... (<;(x)clone(x).I,!1S)

!1S.!1S r (l=<;(x)clone(x).l] -- [/=0).00 by (Red Object)


(oo.(x ...... [I=O]) r x -- [1=0)'00 by (Red x)
[ ~~~(X"""[I=O]) r clone(x) - (1=1)'01 (Red Clone)

ol.(x ..... (I=O)) r clone(x).I-- ?? by (Red Select)


oo.(x ...... (I=O)) r clone(x).l-- ?? (Red Select)
!1S.!1S r (l=~(x)clone(x).l].l-- ?? (Red Select)
Another sort of incomplete derivation tree arises from dynamic error conditions.
In the next example the error consists in attempting to invoke a method from an object
that does not have it.

f!1S'!1S r [] -- []'!1S by (Red Object)


STUCK
!1S.!1S r [].l- ?? (Red Select)
The final example illustrates method updating, and creating loops in the store:
00 ! O...... (~(x)x.l~~(y)x,!1S)
01 ! O..... (~(y)x, (x ...... [I=O)))
10. UNTYPED IMPERATNE CALCULI 139

¢.¢ r- [1=~(x)x.l~~(y)X) -- [1=0)'00 by (Red Object)


[ (Oo.(x~[I=OJ) r- x v-+ [1=0).00 by (Red x)
Oo·(x ..... [I=OJ) r- x.l~~(y)X -- [/=0),01 (Red Update)
¢.¢ r- [l=~(x)x.I~~(y)x).1 -- [1=0].01 (Red Select)
The store 01 contains a loop, because it maps the index 0 to a closure that binds the vari-
able x to a value that contains index o. Hence an attempt to read out the result of
[l=~(x)x.l~~(y)x).1 by "inlining" the store and stack mappings would produce the infi-
nite term [/=~(y)[/=~(y)[/=~(y) .. .lJl.
The potential for creating loops in the store is characteristic of imperative seman-
tics. Loops in the store complicate reasoning about programs and, as we see in the next
chapter, they also demand special attention in the treatment of type soundness.
11 FIRST-ORDER IMPERATIVE 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

(Type Object) (Ii distinct)


E I- Bi 'v'iE1..n
E I- [li:Bi if1..n]

(Sub RefJ) (Sub Trans) (Sub Object) (Ii distinct)


EI-A E I- A <: BEl- B <: C E I- B; 'v'iE1..n+m
E I- A <: A EI- A <: C

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
142 PART 1. UNTYPED AND FIRST-ORDER CALCULI

(Val Subsumption) (Val x) (Val Object) (where A == [/;:B; ;<1.."))


E f- a : A E f- A <: B E', x:A, E" f- <> E, Xi:A f- bi : Bi 'ViEl..n
Ef-a : B E', x:A, E" f- x : A E f- [li=<;(Xi)b i iE1..n] : A

(Val Select) (Val Update) (where A == [/;:Bi ;<1.."))


E f- a: [li:Bi iE1..n] jE l..n E f- a : A E, x:A f- b : Bj jE l..n
E f- a.lj : Bj E f- a.lj*<;(x)b : A

(Val Clone) (where A == [/;:B; ;<I..n)) (Val Let)


Ef-a:A E f- a : A E, x:A f- b : B
E f- clone(a) : A E f- let x=a in b : B

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.

11.2 Examples of Typings


This section illustrates how to type some imperative examples.
We use impA<;f of Section 10.3 for writing terms. When we claim that a term has a
type, we mean that its translation into imp<; has the corresponding translated type in
the type system of Section 11.1 . A function type A~B is translated as an object type
[arg :A, val:B] .

11.2.1 Movable Points


Trivial as it may seem, the example of movable points has been a notorious source of
difficulties in functional settings (see Section 9.5). These difficulties have resulted in the
use of sophisticated type theories. In an imperative setting, however, some of these dif-
ficulties can be avoided altogether.
Consider one-dimensional and two-dimensional points, with integer coordinate
fields (x and y) and methods that modify these fields (mv_x and mv-y). The origin
points are:
11. FIRSf-ORDER IMPERATIVE CALCULI 143

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.

11.3 Classes and Global Behavior Change


Our treatment of traits and classes carries over to imperative calculi, with some inter-
esting twists.

11.3.1 Classes and Update


Let us consider an appropriate variant of the classes c and c' of Section 6.6.1:
let c =
[new=c;(z)[li=C;(s)z .li(s) iE!.."],
li=C;(Z)A(S )bi iEl .."];
let c' =
[new=c;(z)[ li=C;(s )Z.li(S) iE 1..n+m],
IFC;(z)c.lj jE1..n,
h=C;(Z)A(S)bk kEn+!..n+m];
The class c evaluates to a set of locations [new=LQ, li=li iE!..n] pointing to closures for
new and the pre-methods Ii. When c.new is invoked, a set of locations is allocated for the
new object, containing closures for its methods. These closures contain the code
C;(S)Z.li(S), where z is bound to [new=lo, li=li iE!.."]. When a method of the new object is
invoked, the corresponding pre-method is fetched from the class and applied to the
object.
When the pre-method Ij is inherited from c to c', the evaluation of c.lj is suspended
by C;(z). Therefore, whenever the method Ij is invoked on an instance of c', the pre-
method Ij is fetched from c. The binders C;(z) are introduced uniformly in order to sus-
pend evaluation and to achieve this dynamic lookup of pre-methods inherited from c.
When c'.new is invoked, the methods of the new object refer to c' and, indirectly, to c.
11. FIRS[-ORDER IMPERATIVE CALCULI 145

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

let CP2 : C/ass(P2) =


[new = <;(z)[ ... ],
x = <;(z) CPI .X,
y = <;(z) A(S) 0,
mv_x = <;(z) cpI.mv_x,
mv~ = <;(z) A(S) A(dy) s.y:= s.y+dy]

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

11.4 Subject Reduction


We show that the operational semantics of Section 10.5 is consistent with the type sys-
tem of Section 11.1. We use an approach based on subject reduction, adapting the tech-
niques recently developed by Tofte, Wright and Felleisen, Leroy, and Harper [69, 77, 120,
128]. This approach yields manageable proofs and deals with typing rules that seem
hard to justify with other approaches (e.g., denotational semantics).

11.4.1 Typings with Stores


In order to prove a subject reduction theorem, we need to be able to give types to
results. Unfortunately, the typing of results is delicate. We would not be able to deter-
mine the type of a result by examining its substructures recursively, including the ones
accessed through the store, because stores may contain loops. Store types, introduced
next, allow us to type results independently of particular stores. This is possible
because type-sound computations do not store results of different types in the same
location. Next we formalize store types and other notions necessary for the proof of
subject reduction.
A store type ~ associates a method type to each store location. A method type has
the form [li:Bi iEI.."]::::>Bj , where [li:Bi iE!..") is the type of self, and Bj is the result type, for
jEl..n . If~(l) = A::::>B is the method type associated with l in~, then we define ~I(l) ~ A
and ~(l) ~ B.
The statement of subject reduction relies on a new judgment, result typing:
11. FIRST-ORDER IMPERATIVE CALCULI 147

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

(Store Type) (l; distinct)


I:: M; E Meth V'iEl..n
l;-Mi iEI.." I:: <>
148 PART I. UNTYPED AND FIRST-ORDER CALCULI

(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]

(Stack j1l Typing) (Stack x Typing)


1:Fo I: F 5 : E I: F v : A xtdom(E)
I: F 5, x ...v : E, x:A

(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.

11.4.2 Proof of Subject Reduction


We state two lemmas, and then state and prove the subject reduction theorem. Note
that, because of our use of stacks, there is no need for a substitution lemma.
We say that I:' is an extension of 1: (and writel:' ~ 1:) iff dom(1:) c;;; dom(l:') and for
all LEdom(1:), 1:'(L) = I:(L).
Lemma 11.4-1
If 1: F 5 : Eand I:' F 0 with l:' ~ I:, then l:' F 5 :E.
o
Lemma 11.4-2 (Bound weakening)
If E, x:D, E' I- ~ and E I- D' <: D, then E, x:D', E' I- ~.

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 hypothesis E I-- x : A A 1: F 0 A dom(o) = dom(1:) /\ 1: F S', x ..... [li=li ifl..n), S" : E.


Because of E I-- x: A, we must have E;: E', x:A t , E" for some At <: A. Now 1: F S',
X..... [li=li ifl..n), S" : E must have been derived via several applications of (Stack x
Typing) from, among others, 1: F [li=li iEl..n) : At.
Take 1:t;: 1:. We conclude 1:t F 0 A dom(o) = dom(1:t) A 1:t F [/i=li ifl..n) : At A At <: A.
Case (Red Object) (Ii, li distinct)
o.S I-- <> li~dom(o) V'iE1..n
o.S I-- [li=C;(Xi)b i if1..n)_ [/i=li iE1 .. n).(0, li ...... (C;(Xi)bi,S) ifl..n)

By hypothesis E I-- [li=C;(Xi)b i ifl .. n) : A A 1: F 0 A dom(o) = dom(1:) A 1: F S : E. Because


of E I-- [li=C;(Xi)bi iEl..n) : A, we must have E I-- [li=C;(Xi)b i ifl..n) : [/i:Bi if1..n) by (Val
Object), for some [/i:Bi if1..n) <: A. Take At;: [/i:Bi if1..n].
Take 1:t ;: 1:, ljl-+(At~Bj) jfl..n; by (Store Type) we have 1:t F <>, because ljMom(o),
and hence lj~dom(1:), and because F At~Bj E Meth for jE1..n.
(1) Since 1:t is an extension of 1:, by Lemma 11.4-1 we also have 1:t F S : E. Since E I-
[li=C;(Xi)b i if1..n) : At, we must have E, xi:At I-- bi : B& that is, E, Xi:1:t l(li) I-- bi : 1:t 2(li).
(2) We have that 0 is of the form Ek..... {C;(Xk)bluSk) kE1..m . Now 1: F 0 must come from the
(Store Typing) rule, with 1: F Sk: Ek and Elu Xk:1:1(£k) I-- bk: 1:2(Ek). By Lemma 11.4-1,
1:t F Sk : Ek; moreover, Elu Xk:1:\(Ek) I-- bk : 1:t 2(Ek), because 1:t (Ek) = 1:(Ek) for kE l..m
since dom(o) = dom(1:) = {Ek kE1..m} and 1:t extends 1:.
By (1) and (2), via the (Store Typing) rule, we have 1:t F (0, li ..... (C;(Xi)bi,S) if1..n). Since
1:t F <> and 1:t ;: 1:, lj ..... (At~Bj) jf1..n, we have 1:t F [li=Li iEl..n) : At by the (Result
Object) rule.
We conclude 1:t ~ 1: A 1:t F (0, l;>-+(C;(Xi)bi,S) iEl ..n) A dom(o, Li ...... (C;(Xi)bi,S) if1..n) =
A 1:t F [li=Li if!..") : At A At <: A.
dom(1:t)
Case (Red Select)
o·S I-- a ~ [/i=li if1..n).d d(lj) = (C;(xj)bj,S') xj~dom(S') jE1..n
0' .(S', Xjl-+[li=li if!..n]) I-- bj - v.d'
o.S I-- a.lj v-+ v.d'

By hypothesis E I-- a.li: A A 1: F 0 A dom(o) = dom(1:) /\ 1: F S : E. Since E I-- a.li: A, we


must have E I-- a : [lj:Bj' ... ), for some [lrBi' ... ) with Bi <: A.
150 PART I. UNTYPED AND FIRS[-ORDER CALCULI

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»

By hypothesis E f- a.lj:.=~(x)b : A 1\ :E 1= 0 1\ dom(o) = dom(:E) 1\ :E 1= 5 : E. Since E f-


a.lj:.=q,(x)b: A, we must have E f- a: (lj:Bj, ... ) and E, x:[/j:B j, ... ) f- b : Bj for some [/j:Bj,
... ) <: A.
By induction hypothesis, since E f- a : (lj:Bj, ... ) 1\ 0.5 f- a -- [li=Li iEl .. /I).a' 1\ :E 1= a 1\
dom(o) = dom(:E) 1\ :E 1= 5 :E, there exist a type At and a store type :Et such that}:t ~
:E I\}:t 1= 0' 1\ dom(o') = dom(:Et) I\}:t 1= [/i=Li iE1../I) : At 1\ At <: [/j:B j, ... ).
By assumption LjEdom(a'), hence LjEdom(:Et ). But}:t 1= (/i=Li iE\../I) : At must have been
derived via (Result Object) from }:t\(Li) == (li:}:t 2(Li) iEI../I) == At for all iE1..n. Hence,
since At <: [/j:B j, ... I, we have :Et 2(Lj) = Bj .
(1) We have:Et 1= 5: E by Lemma 11.4-1. We also have E, x:At f- b: Bj by Lemma 11.4-
2 (from E, x:[/j:Bj, ... 1f- b : Bj and At <: [lj:B j, ... )), that is, E, X::E\(Lj) f- b: }:t2(Lj)'
(2) Since }:t 1= 0' must come from (Store Typing), 0' is of the form Ekl-+(~(Xk)b",Sk) kE Lm,
and for all k such that Ek '" Lj and for some Ek we have:Et 1= Sk: Ek, and Ek, Xk::Et\(Ek)
f- bk : }:t2(Ek).
Take at == 0' .Lj.... (~(x)b,S). Then by (1) and (2) we have :Et 1= ot, by the (Store Typing)
rule. Since dom(a') = dom(ot), we have also dom(at ) = dom(:Et ).
We conclude:Et ~ :E 1\ :Et 1= ot 1\ dom(ot) = dom(:Et) 1\ }:t 1= [/i=Li iE!../I) : At 1\ At <: A,
with the last conjunct derived from At <: [/j:Bj, ... 1and [/j:B j, ... ) <: A by transitivity.
11. FIRST-ORDER IMPERATIVE CALCULI 151

Case (Red Clone) (l;' distinct)


0.5 f- a -- [li=li iE!.."].o' liEdom(o') li';.dom(o') \tiELn
0.5 f- clone(a) -- [li=l;' iE!.."].(o', l/-o'(li) iEI ..")

By hypothesis E f- clone(a): A 1\ I: F 0 1\ dom(o) = dom(I:) 1\ I: F 5: E. Since E f- clone(a)


: A, we must have E f- a : A.
By induction hypothesis, since E f- a: A 1\ 0.5 f- a -- [li=li iE!..n].o' 1\ I: F 01\ dom(o)
= dom(I:) 1\ I: F 5 : E, there exist a type At and a store type I:' such that I:' ~ I: 1\ I:' F
0' 1\ dom(o') = dom(I:') 1\ I:' F [li=li iEI .."] : At 1\ At <: A.
Let I:t ;: (I:', l/....... I:'(li) iE!..") and ot ;: (0', li'-o'(li) iE1.."). We have I:t F <> by (Store
Type) because li';.dom(o') = dom(I:'), 1.;' are all distinct for iELn, and I:' F <> is a pre-
requisite of I:' F 0'.
We conclude:
• At <: A.
• I:t ~ I:, because I:' ~ I: and I:t ~ I:'.
• dom(ot) = dom(r.t), by construction and dom(o') = dom(I:').
• I:t F ot. Since I:' F 0' must come from (Store Typing), 0' has the shape Ek ....... (~(xdbIoSk}
kE1..m, and for all kELm and for some Ek we have I:' F Sk: Ek and Elo Xk:I:'I(Ek) f- bk:
I:'2(Ek). Then we also have Elo Xk:I:\(Ek) f- bk: I:t 2(Ek), and by Lemma 11.4-1 I:t F Sk :
Ek. Let/: Ln--"7Lm be E- 10l, so that for all iE1..n, li = Efti). We have Efti), xfti):I:'I(Ej{i)
f- bfti) : I:' 2(Efti) for iE 1. n,
. so Efti), Xfti):I:' I(li) f- bfti) : I:' 2(li). Moreover, since I:'(li) =
I:t(li'), we have Ej{i), Xfti):I:\(li') f- bfti) : I:t 2(l;'). The result follows by (Store Typing)
from I:t F Sk: Elo and I:t F Sfti): Eft i ), and Elo Xk:I:\(Ek) f- bk : I:t 2(Ek) and Efti), xfti):I:tl(l;')
f- bj(i) : I:t 2(li'), for kE Lm and iE Ln.
• I:t F [li=l;' iE!.."] : At. First, I:' F [li=li if!.."] : At must come from the (Result Object)
rule with At;: I:'I(li);: [li:I:'2(li) iE1.."] for iE1..n, and I:' F o .But I:t(l/);: I:'(li) for
iE Ln. So I:tl(l/);: [li:I:t 2(l/) iE!.."] ;: At, and we obtain I:t F [l j =l/ iE!.."] : [lj :I:t 2(l/) if!.."]
by (Result Object).
Case (Red Let)
0.5 f- c -- v'·o' 0'.5, X....... v' f- b -- v" .0"
0·5 f-let x=c in b -- v"·o"

By hypothesis E f-let x=c in b : A 1\ I: F 0 1\ dom(o) = dom(I:) 1\ I: F 5: E. Since E f-let


x=c in b : A, we must have E f- c : C for some C, and E, x :C f- b : A.
By induction hypothesis, since E f- c: C 1\ 0.5 f- c -- v'.o' 1\ I: F 0 1\ dom(o) = dom(I:)
1\I: F 5 :E, there exist a type C and a store type I:' such that I:' ~ I: 1\ I:' F 0' 1\
dom(o') = dom(I:') 1\ I:' F v' : C 1\ C <: C.
152 PART I. UNTYPED AND FIRST-ORDER CALCULI

By Lemma 11.4-1, ~' 1= 5 : E, hence by (Stack x Typing)~' 1= 5, x>-+v' : E, x:C'. From


E, x:C f- b: A, we obtain E, x:C' f- b: A by Lemma 11.4-2.
By induction hypothesis, since E, x:C' f- b : A 1\ 0' .5, x>-+v' f- b -- v " ·o" I\~' 1= a' 1\
dom(o') = dom(~' ) 1\ ~' 1= 5, x>-+v' : E, x:C', there exist a type At and a store type ~t
such that ~t ~ r 1\ l:t 1= 0 " 1\ dom(o" ) = dom(l:t) 1\ l:t 1= v" : At 1\ At <: A.
We conclude that l:t ~ l: (by transitivity), l:t 1= 0" with dom(o") = dom(l:t), and l:t 1=
v" : At with At <: A.
o
Corollary 11.4-4
If flS f- a : A and flS.flS f- a "... v.o, then there exist a type At and a store type l:t such that
l:t 1= 0 and l:t 1= v : At, with At <: A.
o
Therefore, if a term has a type and the term reduces to a result in a store, then the
result can be assigned that type in that store. That is, if a term produces a result, it does
so by respecting the type that it had been assigned statically.
As discussed in Section 7.5.2, this statement is vacuous if the term does not pro-
duce a result. This can happen either because reduction diverges, or because it gets
stuck (no rule is applicable at a certain stage).
For each term there is at most one rule whose conclusion matches the syntactic
form of the term, and hence is potentially applicable to the term. The rule (Red x) is
applicable to x unless x is not defined in the stack. Assuming that b reduces to v, the
rule (Red Update) is applicable to b.lj~C;(x)c provided v has Ij . The applicability of the
rule (Red Select) is determined with an analogous condition. Assuming the appropri-
ate subterms converge, the rules (Red Object), (Red Clone), and (Red Let) are always
applicable to terms of the corresponding forms.
Examining these cases, and adapting the proof of Theorem 11.4-3, we can deduce
that the reduction of a well-typed term in a well-typed store cannot get stuck (although
it may diverge). We omit a proof of this claim.
12 A FIRST-ORDER LANGUAGE

In Part I of this book, we have introduced a sequence of first-order object calculi of


increasing sophistication. It may be time to assess the contributions that these calculi
bring to the task of modeling programming language constructs. For this purpose, we
invent and study a simple language named O-I.
We describe the syntax of 0-1 and its typing rules; we argue that these rules are
sound by showing how 0-1 can be translated into a first-order object calculus. The
object calculus in question, though never assembled explicitly, combines many of the
notations and rules of the previous chapters. 0-1 includes both class-based and object-
based constructs, first-order object types with subtyping and variance annotations,
recursion, and a typecase construct. The study of 0-1 exemplifies how we can use
object calculi for analyzing the constructs of object-oriented languages.

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.

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
154 PART I. UNTYPED AND FIRST-ORDER CALCULI

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

Syntax of 0-1 terms

a,b,c ::= terms


x variable
object(x:A) li=b i i€!..n end direct object construction
a.l field selection / method invocation
a./:= b update with a term
a.1 := method(x:A) bend update with a method
newc object construction from a class
root root class
subclass of c:C with(x:A) subclass
li=b i i€n+l..n+m additional attributes
override l;=b i i€Ovr,;!..n end overridden attributes
cAl(a) class selection
typecase a when (x:A)b j else b2 end typecase

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

CPoint g, Object(X)[x: Int, c: Color, eq+: Point~Bool, mv+: Int~Pointl


12. A FIRSf-ORDER LANGUAGE 157

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

of interfaces and implementations and would obstruct the separate compilation of


classes.
In writing the overriding mv method, one option would be to make a textual copy
of the code of mv into cPointClass2, so that it could be adequately typed in the new con-
text. However, even without copying it, we can reuse that code by a call to super.mv,
which invokes the mv method of the superclass. The result of this call needs to be a
color point, and the necessary typing is achieved with a typecase.
In contrast to the code based on CPoint, typecase is no longer needed after a color
point is moved. For example, we can write:
cPoint2 : CPoint2 ~ new cPoint2Class
movedColor2 : Color ~ cPoint2.mv(I).c
In sum, by switching from CPoint to CPoint2 we have shifted typecase from the code
that uses color points to the code for the class cPoint2Class. This shift may be attractive,
for example because it may help in localizing the use of typecase.
As this example illustrates, 0-1 allows subtyping without inheritability: there are
types A and B (namely, CPoint2 and Point) such that A <: B, and such that a class for A
can be constructed as a subclass of a class for B, but with the requirement that some
methods be overridden in the subclass (namely, mv).

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

The rules for environments are standard:


Environments
(Env!2l) (Env X<:) (Envx)
E I- A X;'dom(E) E I- A x;'dom(E)
E, X<:A I- 0 E, x:A I- 0

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)

For subtyping, we have:


Subtyping
(Sub RefJ) (Sub Trans) (Sub X) (Sub Top)
EI-A E I- A <: BEl- B < : C E', X<:A, En I- 0 EI-A
E I- A <: A E I- A <: C E', X<:A, En I- X <: A E I- A <: Top

(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 '

(Sub Invariant) (Sub Covariant) (Sub Contravariant)


EI-B E I- B <: B' UE [o,. } E I- B' < : B \)E (O,- )
E I- °B <: 0 B E I- U B <: + B' E I- uB <: - B'

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

El-a:B E', x:A, En f- X : A

(Val Object) (where A = Object(X)[/twBj/X) jEl..n))


E, x:A I- b;: B;tA} 'v'iE1..n
E I- object(x:A) I;=b; ;E1..n end: A

(Val Select) (where A = Object(X)[I;lJj:Bj/X) ;El..n))


El-a:A V;E/o,+} jE1..n
E I- a.l; : B;Ma

(Val Update) (where A "" Object(X)[I,'Uj:Bj/X) jEt ..n))


E I- a: A E I- b: B;MD V;E/o,-} jEl..n
E I- a.l; := b : A

(Val Method Update) (where A"" Object(X)[I,'Uj:B;/X) ;EI.."])


El-a:A E,x:Al-b:B;{AD V;E/o,-} jEl..n
E f- a.l; := method(x:A)b end: A

(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)

E f- c' : Class(A') E I- A <: A'


EI- B;'M'D <: B;(AD 'v'iEl..n-Ovr
E, x:A f- b; : B;gA) 'v'iEOvrun+l..n+m
E I- subclass of c':Class(A') with(x:A) l;=b; ;En+1..n+m override l;=b; ;EOvr end: Class(A)
162 PART I. UNTYPED AND FIRS[-ORDER CALCULI

(Val Class Select) (where A == Object(X)[I,'\Ji:BiIXI iEt ..n))


Er-a: A E f- c: Class(A) jEl..n
E f- cAlj(a) : BjlAB

(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

(Val Update) (where A == Object(X)[I.WBi(XI iEl..nJ)


E f- a: A E f- b: BjlA} tJjE[O,-} jEl .. n
E f- (a:A).lj := b : A

It is straightforward to translate 0-1 to this type-enriched variant of 0-1, by induction


on derivations.
We show a translation into a functional target calculus; a similar translation could
be given into an appropriate imperative calculus. We build a target calculus from many
of the rules studied in Part I. We take FOb1 <:11 from Chapter 9, modified with the stron-
ger rule (Sub Rec') of Section 9.2 instead of the standard rule (Sub Rec), and extended
with the rules for variance annotations of Chapter 8, and with (Val Typecase) from Sec-
tion 9.7. The use of (Sub Rec') is necessary for validating the rule for subtyping object
types in 0-1; in particular it is necessary for the subtyping CPoint2 <: Point in the exam-
ple of Section 12.3.
At the level of types, the translation is reasonably simple. We write «A» for the
translation of A. We map an object type Object(X)[I{ui:Bi iE1..n) to a recursive object type
J.l(X)[lj'IJi:«Bi» iE1..n). Much as in Section 8.5, we map a class type Class(Object(X)
[l.wB;{X} iE1..nJ) to an object type that contains components for pre-methods and a new
component. We assume that the label new does not occur in the source language.

Translation of 0-1 types

«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]

This translation of types induces a translation of environments:

Translation of 0-1 environments

«~» ~ ~
«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

unused y and an appropriate A. We obtain the following preliminary version of the


definition:
Preliminary translation of 0-1 terms

«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

«a.l := method(x:A) b{x) end» ~ fo/d(«A»,unfold(«a»).l~~(x:<<A»t)<<bHfold(<<A»,xm


=
where A Object(X)[I,1Ji:Bi iEl .. n]
«new c» ~ «c».new
«root» ~ [new=~(z:[new+ : Il(X)[mfold(Il(X)[],[])]
«subclass of c':Class(A') with(x:A) li=b i iEn+1..n+m override li=bi iEOvr end» ~
[new=~(z:«Class(A)>> )fold(«A», [li=~(s:«A» t)z.li( jold( «A»,s» iE l..n+m]),
li=~(z:«Class(A)>> )«c'».I; iE I..n-Ovr,
li=~(z:«Class(A)>> )A(X:«A» )«b;» iEOvrvn+ I..n+m]
= =
where A Object(X)[/,1Ji:Bi iE1..n+m] and A' Object(X)[/,1Ji':B;' iE1..n]
and z~FV( «c'» )vFV( «b;» iEOvrvn+ l..n+m)
«cl\l(a)>> ~ «c».l(<<a»)
«typecase a when (x:A)b1 else b2 end» ~ typecase «a» I (x:«A»)«b 1» I «b2»

Combining the translations of types, environments, and terms, we can translate


whole judgments, for example mapping E I- <> to «E» I- <> and E I- a : A to «E» I- «a» : «A».
We write «E I- ~» for the translation of E f- ~. As a soundness result, we obtain that if E
I- ~ is derivable in the source language then «E I- ~» is derivable in the target calculus.
This result can be proved by induction on derivations.
Thus we have succeeded in using object calculi as a platform for explaining a rel-
atively rich object-oriented language and for validating its type rules.
PART II
Second-Order Calculi
13 SECOND-ORDER CALCULI

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.

13.1 The Universal Quantifier


We begin by reviewing simple universal quantification over types, of the kind tradi-
tionally used to model parametric polymorphism. We then consider bounded quanti-
fication, which combines parametric polymorphism with subtyping.

13.1.1 Parametric Polymorphism


As before, we use the notation b{X} and B{X} to single out the occurrences of X free in
b and in B. Moreover, we use our substitution conventions for types, so bRA» stands for
bRX~A» and BRA» stands for BiXf-A» when X is clear from context.
A term A(X)b{X} represents a term b parameterized with respect to a type variable
X; we call this a type abstraction. Correspondingly, a term a(A) is the application of a
term a to a type A; we call this a type application. Thus bRA» is an instantiation of the type
abstraction A(X)b{X} for a specific type A, and can be obtained as the result of a type
application (A(X)b{X})(A). We write V(X)B{X} for the type of those type abstractions
A(X)b{X} that for any type A produce a result bRA» of type BRA».
For example, we may write:

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
170 PART II. SECOND-ORDER CALCULI

id : V(X) X~X ! A(X) A(X:X) X the polymorphic identity function


id(Int) : Int~Int its instantiation to the integer type
id(Int)(3) : Int its application to an integer
Polymorphism is obtained from the 6.x fragment for type variables of Section 9.1,
plus the following fragments for universal quantifiers.
6.",

(Type All) (Val Fun2) (Val Appl2)


E, XI- B E, X I- b: B E I- b : V(X)B{X} E I- A
E I- V(X)B E I- A(X)b : V(X)B E I- b(A) : BlAB

(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

(Eval Beta2) (Eval Eta2)


E I- A(X)b{X} : V(X)BIX} E I- A E I- b : V(X)B Xtdom(E)
E I- (A(X)b{X})(A) H biAJ : BCAB E I- A(X)b(X) H b : V(X)B

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).

13.1.2 Bounded Parametric Polymorphism


We proceed to second-order calculi with subtyping. We add a subtyping judgment, as
for first-order systems, and we extend universally quantified types 'V(X)B to bounded
universally quantified types 'V(X<:A)B, where A is the bound on X. The bounded type
abstraction A(X<:A)b{X} has type 'V(X<:A)B{X} if, for any subtype A' of A, the instanti-
ation bIA'H has type BIAl
We reuse the "'<:x fragment of Section 9.2, where type variables have bounds in
environments, and add:

"'<: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'

(Val Fun2<:) (Val AppI2<:)


E, X<:A f- b : B E f- b: 'V(X<:A)B{X} E f- A' <: A
E f- A(X<:A)b : 'V(X<:A)B E f- b(A') : BIA 'H

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.

(Eq Fun2<:) (Eq AppI2<:)


E, X<:A f- b H b': B E f- b H b' : 'V(X<:A)B{X} E f- A' <: A
E f- A(X<:A)b H A(X<:A)b': 'V(X<:A)B E f- b(A ') H b'(A ') : BM 'H

(Eval Beta2<:) (Eval Eta2<:)


E f- A(X<:A)b{X} : 'V(X<:A)B(X} E f- C <: A E f- b : 'V(X<:A)B X;'dom(E)
E f- (A(X<:A)b(X})(C) H bKH : BaCH E f- A(X<:A)b(X) H b: 'V(X<:A)B
172 PART II. SECOND-ORDER CALCULI

We obtain the calculi:


~ !::"x U 11-4 U 6<: u ll<:x u 6<:-4 U d<:v
~ !l.x U !l.Ob U fl<:u Il<:x u .A<:Ob U ~:v
~ F<: U dOb U 1l<:Ob

F<:J.' ~ F<: v 6.<:11


Ob<:J1 ~ Ob<: U ~:Jl
FOb<:11 ~ FOb<: v 6.<:11
F<: is described in [44], but we assume only the simpler equational theory used in [56],
as given by the fragment 6.=<:'1'

13.1.3 Structural Update


While adding sub typing and polymorphism to our calculi, we have consciously chosen
not to modify the 6.0b fragment. However, it is tempting to change the (Val Update)
rule as follows:
(Val Structural Update) (where A'" [li:Bi i.I.."])
Ef-a:C Ef-C<:A E,x:Cf-b:Bj jEl..n
E f- a.lj'iFc,,(x:C)b : C
The difference between (Val Update) and (Val Structural Update) can be seen when C
is a type variable, as in the following example:
A.(C<:[I:Nat]) A.(a:C) a.l:=3 : I;t(C<:[I:Nat]) C--?[I:NatJ via (Val Update)
A(C<:[I:Nat)) A(a:C) a.l:=3 : I;t(C<:[I:Nat]) C--?C via (Val Structural Update)
The new rule (Val Structural Update) appears intuitively sound. It implicitly relies
on the invariance of object types, and on the "structural subtyping" assumption that
every subtype of an object type is an object type. Such an assumption is quite easily
realized in programming languages and can be shown to hold in formal systems such
as ours.
However, structural subtyping is not satisfied by the semantics of Chapter 14,
where the subtype relation is simply the "unstructured" subset relation. In that seman-
tics, the type I;t(C<:[I:Nat)) C-7C contains only an identity function and its approxima-
tions, and hence does not contain the preceding program, A(C<:[I:Nat)) A(a:C) a.l:=3.
This fact is not a special property of our semantics, but rather a consequence of the
parametricity of polymorphic functions [108]. A semantics that satisfies structural sub-
typing could probably be devised, but would be more complex and might have a rather
syntactic flavor (see [43] for a first-order example of such a semantics).
This semantic difficulty suggests that we should proceed with caution. Hence we
do not adopt the rule (Val Structural Update) at once, but we return to the topic of
structural assumptions later on, in Section 15.7 and in Chapter 16.
13. SECOND-ORDER CALCULI 173

13.2 The Existential Quantifier


We introduce the bounded version of the existential quantifier directly. The
unbounded version is a special case.
The existentially quantified type 3(X<:A)B{X} is the type of the pairs (A',b) where
A' is a subtype of A and b is a term of type BgA'D. The type 3(X<:A)B{X} can be seen as
a partially abstract data type with interface B{X} and with representation type X known
only to be a subtype of A [45, 93J. It is partially abstract in that it gives some information
about the representation type, namely, a bound. The pair (A ',b) describes an element of
the partially abstract data type with representation type A' and implementation b. In
order to be fully explicit, we write the pair (A ',b) m?re verbosely in the form:
pack X<:A=A' with b{X}:B{X}
where X<:A=A' indicates that X<:A and X=A'. This notation expresses that the repre-
sentation type X is A', and it uniquely determines the type 3(X<:A)B{X} .
An element c of type 3(X<:A)B{X} can be used in the construct:
open cas X<:A,x:B{X} in d{X,x}:D
where d has access to the representation type X and the implementation x of c, and
must produce a result of a type D that does not depend on X. The requirement that X
does not occur in D corresponds to the informal requirement that the type abstraction
be respected. At evaluation time, if c is (A',b), then the result is dM',bD of type D. For
example, we may write:
p : 3(X<:Int)Xx(X~X) £ pack X<:Int=Nat with (O,SUCCNal): Xx(X~X)
a: Int £ open p as X<:Int,x:Xx(X-7X) in snd(x)lfst(x»:Int
and then a = l.
We adopt the following rules for existentially quantified types:

~:3

(Type Exists<:) (Sub Exists)


E, X<:A f- B E f- A < : A' E, X<:A f- B <: B'
E f- 3(X<:A)B E f- 3(X<:A)B <: 3(X<:A')B'

(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 Unpack<:) (where c .. pack X<:A;C with b{X}:B{X))


E f- c : 3(X<:A)B{X) E f- D E, X<:A, x:B{X) f- d{X,x) : D
E f- open cas X<:A,x:B{X) in d{X,x):D H dIC,b(C&» : 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

F<: g, F<: v ~<:3 F<:fl g, F<:fl v ~<:3


Ob<: g, Ob<:V~:3 Ob<:fl g, Ob<:fl V~<:3
FOb<: g, FOb<: V~<:3 FOb<:fl g, FOb<:flV~: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

Scoping for the FOb<:J! calculus


FV(X) ~ {X}
FV(Top) ~ {}
FV([li: Bi iE!..n]) ~ V iE Ln FV(B i )
FV(A-?B) ~ FV(A) v FV(B)
FV(J.1(X)A) ~ FV(A)- {X}
FV(V(X<:A)B) ~ FV(A) v (FV(B) - (X})
FV(3(X< :A)B) ~ FV(A) v (FV(B) - (X})

FV(~(x : A)b) ~ FV(A) v (FV(b}-(x})

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

FV(a.I~c;(x:A)b) ~ FV(a) u FV(C;(x:A)b)


FV(A(X:A)b) ~ FV(A) u (FV(b)-{x})
FV(b(a» ~ FV(b) u FV(a)
FV(jold(A,a» ~ FV(A) u FV(a)
FV( unfold(a» ~ FV(a)
FV(A(X<:A)b) ~ FV(A) u (FV(b)-{X)
FV(b(A» ~ FV(b) u FV(A)
FV(pack X<:A=C with b:B) ~ FV(A) u FV(C) u «FV(b) u FV(B»)-{X)
FV(open cas X<:A,x:B in d:D) ~ FV(c) u FV(A) u (FV(B)-{X})
u (FV(d)-{X,x}) u (FV(D)-{X})

In Chapter 14 we give a denotational semantics of FOb<:~ that guarantees its


soundness. For the sake of brevity, therefore, we do not prove subject reduction results.
Also for the sake of brevity, we do not prove minimum-types properties.

13.3 Variance Properties


So far we have dealt with variance rather informally. We now give a syntactic defini-
tion of variance that yields the expected formal properties.
Recall that the notation A{X) identifies all the free occurrences of X in A, so that
A{BH is the result of substituting B for X in A. We extend this convention as follows. The
notation A{X+) represents the assertion that X occurs positively (or not at all) in A. Sim-
ilarly, A{X-) represents the assertion that X occurs negatively (or not at all) in A. Finally,
A{XO) means that X occurs neither positively nor negatively in A. The following table
defines the notation for variant occurrences.
Variant occurrences
Y{X+) whether X = Yor X"# Y
Top{X+) always
[hBi iel..n]{x+) if X 'f. uiel.. n FV(B i )
(A-7B){X+) if A{X-) and B{X+)
(Il(Y)A){X+) if X = Y, or both A{Y+) and A{X+), or X-;.FV(A)
('v'(Y<:A)B){X+) if X = Y or both A{X-) and B{X+)
(3(Y<:A)B){X+) if X = Y or both A{X+) and B{X+)
Y{X- ) if X"# Y
Top{X-) always
[li:Bi iel..n){x-) if X 'f. UiEl.. n FV(Bi)
(A-7B){X-) if A{X+) and B{X-)
(Il(Y)A){X-) if X = Y, or both A{Y+) and A{X- ), or X;'FV(A)
178 PART II. SECOND-ORDER CALCULI

(V(Y<:A)B){X-) if X = Y or both A{X+) and B{X-}


(3(Y<:A)B){X-) if X = Yor both A{X-} and B{X-)
A{X"} if neither A{X+) nor A{X-)

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

13.4 Variant Product and Function Types, Encoded in Ob<:


Within the first-order object calculus Ob 1<: we were unable to find a full translation of
A-calculi with subtyping, because the variance properties of ~ could not be obtained
with invariant object types only (Section 8.1). One solution was to add variance anno-
tations (Section 8.7).
Now we have at our disposal bounded universal types, which are contravariant in
their bounds, and bounded existential types, which are covariant in their bounds.
Combining these variant constructs with invariant object types, we can define a variant
version of function types as follows:
A-7\t3B ! V(X<:A)3(Y<:B)[arg:X, val:YJ
We obtain A-7\t3B <: A,~\t3B' if A' <: A and B <: B'.
This idea gives rise to an encoding of Fl<: into Ob<:. As before, this encoding is
defined on typing derivations, but for simplicity we write it as a translation of type-
annotated A-terms.
13. SECOND-ORDER CALCULI 179

Translation of the first-order A-calculus with subtyping


P E Var -"7 Ob<:-term
j1l(x) ! x
(p{yra})(x) ! ifx = y then a else p(x)
«j1l» ! j1l
«E, x:A» ! «E», x:«A»
«K» ! (any closed type)
«A-"7B» ! 'v'(X<:«A»)3(Y<:«B»)[arg:X, val:Y]
«XA»p ! p(x)
«bA .... B(aA)>>p !
open «bM«A») as Y< :«B», y:[arg:«A», val:Y]
in (y .arg~c;(x: [arg:«A», val:YJ)«a»p).val: «B»
«A(x:A)bB»p !
A(X<:«A»)
(pack Y<:«B»=«B» with
[arg=c;(x:[arg:X, val:«B»J)x.arg,
val=c;(x:[ arg:X, val:«B» D«b»Plx<-x.nrgl]
: [arg:X, val:YJ)

This translation can be extended trivially to recursive types and to second-order


quantifiers. Hence our largest calculus, FOb qlf can be embedded inside Ob<:w
We can obtain covariant product types, since these can be represented in terms of
universal quantifiers and variant function types in F<:. However, a more direct encod-
ing is available:
A x 33 B ! 3(X<:A)3(Y<:B)[jst:X, snd:Y]
We obtain AX33 B < : A'X33B' if A <: A' and B <: B'.
In (40) it is shown that record types (lj:A j jE1..n) can be represented in F<" using cova-
riant product types. These record types are covariant, in the sense that (lj:Aj jEl .. n+m) <:
(lj:A/ jE1..n) if Aj<: A/ for iEl..n. We can therefore encode covariant record types in Ob<:.
In sum, these encodings confirm the expressiveness of Ob<:. We are able to repre-
sent function, product, and record types, with their desired variance properties, in
terms of the most basic object types and standard bounded quantifiers.

13.5 The Self Quantifier


Within the second-order c;-calculus with bounded quantifiers and recursion, Ob<:,., we
can form an interesting construct that we call the Self quantifier. In this section we
study the formal properties of the Self quantifier independently of objects. In a later
180 PART II. SECOND-ORDER CALCULI

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.

13.5.1 Defining the Self Quantifier


The Self quantifier C; is a combination of recursion and bounded existentials, with
recursion going "through the bound":
C;(X)B ~ ~(Y)3(X< : Y)B (Y not occurring in B)
Given a type B{X}, any type BHA» can be transformed into a related type
3(Y<:A)B{Y» covariant in A. An analogous technique applies to recursive types, and
motivates our definition of the Self quantifier. Given an equation X = B{X}, we trans-
form it into X = 3(Y<:X)B{YB. The solution to this equation, C;(X)B{X}, satisfies the sub-
typing property:
if B{X) <: B'{X}, then C;(X)B{X) <: C;(X)B'{X}
even though we may not have ~(X)B{X) <: ~(X)B'{X) . This property suggests that C; is
preferable to ~ in some situations where sub typing is important, and it is the main
motivation for the Self quantifier.
The elements of C;(X)B{X} can be understood informally as pairs. Modulo an
unfolding, C;(X)B is the same as 3(X<:C;(X)B)B. Hence by analogy with the interpreta-
tion of existential types, C;(X)B{X} can be understood as the type of pairs (C,c) consisting
of a subtype C of C;(X)B{X} and an element c of BeC).
For example, suppose we have an element x of type C;(X)X. Then, choosing C;(X)X
as the required subtype of C;(X)X, we obtain (C;(X)X, x) : C;(X)X. Therefore we can con-
struct:
~(x)(C;(X)X, x) : C;(X)X
Less trivially, and still informally, suppose we want to define a type St of storage
cells, and to build a storage cell st : St having a read method get with result type Nat
and a write method set with parameter type Nat and result type St. We can define:
St ~ C;(X)[get:Nat, set:Nat~Xl
where the set method should use its argument to update the get field. For convenience,
we adopt the following abbreviation to unfold a Self quantifier.
Notation
• A(C) ~ B«C» whenever A == C;(X)B{X} and C <: A .
So, for example, St(St) == [get:Nat, set : Nat~Stl .
To define a storage cell, we are going to use twice the fact that if x : St(St), then (St,
x) : St. (This follows simply by the typing rule for (-,-) explained previously.) First we
need a method body for set that, with self s : St(St) and argument n : Nat, produces a
result of type St. Since s.get:=n has the same type as s, namely St(St), we can use (St,
13. SECOND-ORDER CALCULI 181

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)])

13.5.2 Derived Rules for the Self Quantifier


Beyond these initial and rather informal examples, the best way to understand the Self
quantifier is to look at its specific application to objects in Chapter 15. Still, it is impor-
tant to examine the intrinsic properties of types of the form C;(X)B, because these prop-
erties help in deriving some complex rules for object types with Self types. The rest of
this chapter is therefore dedicated to the study of the abstract properties of types of the
form C;(X)B.
The two basic operations for the Self quantifier are similar to the ones for the exis-
tential quantifier. One operation constructs an element of C;(X)B, given a subtype of
C;(X)B and an appropriate value; it is the composition of pack for existentials and fold for
recursive types. The other operation inspects an element of C;(X)B (as much as possible)
and computes with its contents; it is the composition of unfold for recursive types and
open for existentials.
The operation for constructing elements of type A E C;(X)B{X}, in full generality,
binds a type variable. Hence we need a more complex syntax than the pairing (-,-)
used above. We refine the notation (C,b) to wrap(Y<:A=C)b.
The term wrap(Y<:A=C)b binds C to Yin b, and requires C to be a subtype of A. We
often choose C = A, but the ability to choose C <: A is important, particularly when Cis
a variable. (Section 15.5 shows that this generality has a price.) Within b, the types Y
and C are equivalent. The type of the whole term is A.
The term use cas Y<:A, y:BKn in d:D decomposes a term c of type A into its two
components, naming them Yand y, respectively. The type variable Yis a subtype of A,
and the term variable y has type B[Y». These variables are used in the term d of type D.
The type D cannot contain occurrences of Y.
We define:
The Self quantifier
C;(X)B ~ Jl(Y)3(X<:Y)B
(with Y~FV(B))

wrap(Y<:A=C)b{Y} ~ fold(A,pack Y<:A=C with b{Y} :B[Y»J


(with A E C;(X)B{X), C <: A, b[q : BlCD
use cas Y<:A, y:BHY» in d{Y,y}:D ~ open unfold(c) as Y<:A, y:B[Y» in d{Y,y}:D
(with A E C;(X)B{X), c: A, d{Y,y} : D, Y~FV(D)

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

(Type Self) (Sub Self)


E, X<:Top I- B E, X<:Top I- B <: B'
E I- <;(X)B E I- <;(X)B <: <;(X)B'

(Val Wrap) (where A == <;(X)B(X)) (Val Use) (where A == 9X)B(X})


E I- C <: A E I- b«q : Beq E I- c: A E I- D E, Y<:A, y:B{YII- d: D
E I- wrap(Y<:A=C)b(YI : A E I- use c as Y<:A, y:B{YJ in d:D : D

(Eq Wrap) (where A == 9X)B(XI, A' == 9X)B'(X})


E I- C <: A' E I- A E, Y I- B'CY} <: BIY» E I- b{q H b'«q : B'{C}
E I- wrap(Y<:A=C)b(YI H wrap(Y<:A'=C)b'(YI : A

(Eq Use) (where A == <;(X)B(X))


E I- C H c': A E I- D E, Y<:A, y:Bey! I- d H d': D
E I- use c as Y<:A, y:BIYi in d:D H use c' as Y<:A, y:BIYi in d':D: D

(Eval Unwrap) (where A == 9X)B(XI, c == wrap(Z<:A=C)b(Z})


E I- c: A E I- D E, Y<:A, y:B(Y} I- d(Y,yl : D
E I- use c as Y<:A, y:B(Y& in d(Y,yl :D H dlC,blCn : D

(Eval Rewrap) (where A == <;(X)B(X))


E I- b : A E, y:A f- d(yl : D
E f- use bas Y<:A, y:B«YJ in dlwrap(Y'<:A=Y)ylD H d«b» : D

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.

13.5.3 A Pure Second-Order Object Calculus


We conclude this section by considering a pure second-order object calculus, ~Ob,
based exclusively on object types and the Self quantifier. (See Appendix B.3.)
~Ob ! I:1 x U 1:10b U 1:1<: u ~:x U 1:1<:Ob U 1:1,
The syntax of this calculus is summarized in the following table.
Syntax of the ~Ob calculus
A,B::= types
X type variable
Top the biggest type
[1;:Bi iE1..n] object type (Ii distinct)
<;(X)B Self quantified type
a,b ::= terms
x variable
[/i=C;(Xi:Ai)bi iE1..n] object (Ii distinct)
a.1 method invocation
a.I~c;(x)b method update
wrap(X<:A=B)b wrap
use a as X<:A, y:B in bD use

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

as Chapter 15 demonstrates, the Self quantifier provides an essential second-order fea-


ture of object calculi, namely, the Self type. Interestingly, then, <;Ob is a small second-
order object calculus that covers a spectrum of object-oriented notions.
14 A SEMANTICS

In Section 6.7 we reviewed two syntactic interpretations of objects in terms of records.


In one of them, the self-application semantics, an object is a record of functions; corre-
spondingly, an object type is interpreted as a recursive record type. For example, the
object type Point ~ [x,y:lnt] is interpreted as the record type obtained by solving the
type equation Point = (x,y:Point-')/nt). Unfortunately, this type does not include the
solution of ColorPoint = (x,y:ColorPoint-')/nt, c:ColorPoint-')Color), which is the interpre-
tation of ColorPoint ~ [x,y:lnt, c:Color]. Therefore, the self-application semantics does
not validate subtypings such as Color Point <: Point.
The denotational semantics that we give in this chapter can be understood as a
variant of the self-application semantics. Specifically, we interpret the type Point as the
union of all the solutions to the equations of the form X = (x,y:X-')Int, ... ), including, for
example, X = (x,y:X-')/nt) and X = (x,y:X-')/nt, c:X-')Color). With this definition, Color-
Point is a subtype of Point. The semantics is based on the simple idea just described. On
the other hand, the details of the semantics are fairly intricate, and understanding them
requires some familiarity with denotational techniques; the casual reader should be
able to skip those details.
We describe a semantics for the largest calculus considered in Chapter 13, FOb<:11'
The semantics provides a justification for our type theories and our equational theories,
and to some extent guided us in their choice.
The semantics is based on a metric approach [82]. Following Amadio and Cardone,
we interpret types as complete uniform pers, and in particular we interpret recursive
types as fixpoints of contractive functions on complete uniform pers [17, 46]. It might be
possible to obtain a semantics for FOb q1 with standard O-categorical methods [112]
instead of metric methods. However, even the fragment F<:J1 poses problems, as noted
in [10]. We expect object types to give rise to further interesting difficulties.

14.1 The Untyped Universe


We interpret our calculus in an untyped universe: we map terms to elements of a single
set irrespectively of their types. The assumptions on the universe are fairly standard
and technical (see, e.g., [10, 17,46,47,82]). Essentially, we assume a complete partial order
D that contains a value * and that includes the continuous function space (D-')D) and
the function space (L-')D), where L is a countable set of labels {mo, mj, ... }. We use * to
represent an error, a function in (L-')D) to represent a record, and a record of functions
to represent an object.

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
186 PART II. SECOND-ORDER CALCULI

A suitable D can be constructed by applying the usual "limit of a sequence of iter-


ates" method to solve the domain equation D = W + (D~D) + (L~D)J.' where:
• W is the set {.1, *1 with the order .1 !; *;
• (D--1D) is the complete partial order of continuous functions from D to D;
• (L--1D) is the complete partial order of functions from L to D, and (L--1D)J. is its
lifting;
• + is the coalesced sum operator, so the least elements of the summands are iden-
tified;
• the equation is solved over complete partial orders, up to isomorphism: the
solution D is a complete partial order isomorphic to W + (D--1D) + (L--1D)J.'
It is important for our purposes to make sure that at each stage of the "sequence of iter-
ates" construction we have a finite set. Therefore the iterate Di+l should not contain the
full (L--1Di)J. but only (Li--1Di)J. where Lds a finite subset of L; we take Li = {mo, ..., mil.
Thus Do is {.11 and Di+l is W + (Di--1Di) + (Li--1Di)J.' The solution D is obtained from Do,
Dl, . . by a limiting process.
We do not detail the construction, but instead list some essential requirements. We
need to have a partial order (D,!;) such that:
• (D,!;) is a complete partial order: .1 is its least element and every sequence (Xi)
such that Xo !; Xl !; ... !; Xi !; ... has a least upper bound Ui Xi.
• There are strict, continuous embedding-retraction pairs (el,Tl), (e:uT2), and (e3,T3),
between D and W, (D--1D), and (L--1D)J., respectively.

e1 rl
W D W

e2 r2
(D~D) D (D --1 D)
e3 r3
(L~ D).l D (L ~ D).l

• There is an increasing sequence pn : D--1D of continuous projections with finite


range and with least upper bound the identity. Further, po constantly equals .1.
• Let; denote function composition. Let t denote function restriction (so (ht Li)(m)
is hem) if m E Li and is .1 otherwise). For all i,
Pi+l(e1(*)) el(*)
Pi+1(e2(J)) f2CPi;f; Pi) for! E D--1D
Pi+1(e3(O)) e3((o; Pi)t Li) for 0 E L--1D

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 Types in the Untyped Universe


Having described an untyped universe, we view the types as certain binary relations
on this untyped universe. Intuitively, if A is a type and RA is the associated relation,
then (x,y) E RA means that x and yare equal elements of A. Subtyping is simply inter-
preted as relation containment.
After some preliminaries, Section 14.2.2 introduces several operations on binary
relations and Section 14.2.3 discusses subtyping. Section 14.2.4 provides lemmas about
the union of binary relations, and Section 14.2.5 provides some results about metric
properties.

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.

14.2.2 Semantic Definitions for Type Constructors


First we describe some usual constructions on pers.
The largest cuper in which we are interested is Univ:
Univ ~ (D-{*})x(D-{*})
It relates any two elements different from *.
188 PART II . SECOND-ORDER CALCULI

The function-space operation is given by:


P~Q ~ {(f,g) E (D~D) x (D~D) I for all x, y, if xPy, thenj(x)Qg(y)1

If P, Q E CUPER then P~Q E CUPER.


We can calculate meets and joins for any family of cupers:
niEl Pi ~ niEl Pi
UiEl Pi ~ C(UiEl Pi)
where C(P) is the least cuper that contains P; the cuper C(P) is always defined since the
conditions that describe cupers are monotone closure conditions. If Pi E CUPER for all
i E I, then niEl Pi E CUPER and UiEl Pi E CUPER.
A pair (x,y) is finite if each of x and y is finite; the rank of (x,y) is the greatest of the
ranks of x and y. The restriction of a cuper R to elements of rank no greater than r is
Pr(R) ~ {(x,y) E R I rank of (x,y) ~ rl . A cuper is determined by its finite elements: by uni-
formity and completeness, if pr(R) = p.(T) for all r, then R = T. The distance between two
cupers is 2-r , where r is the minimum rank where the two cupers differ, and it is 0 if the
two cupers are equal:
distance(R,T) ~ max({OI U {2-r I Pr(R) '¢ Pr(T)))

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

«mi:Ti iEI» ! {(.1,.1») v {(O,O') E (L-tD) x (L-tD) I '<IiEI. (o(m;),O'(mi» E Til


The type «mi:Ti iEI)) can be viewed as a record type, with fields mi and types Ti . If Ti E
CUPER for all iEI then ((mi:Ti iEI» E CUPER. Uniformity holds because, if (0,0') E ((mi:Ti
iEI», then (Po(o),Po(o'» = (.1,.1) E ((mi:Ti iEI», and either (Pj+l(o)(mi),pj+l(o')(m;» = (pj(o(mi»,
pj(o'(mi))) or (Pj+l(o)(mi),pjdo')(mi» = (.1,.1), so (Pj+l(O),Pj+l(O'» E ((mi:Ti iEI». Moreover, as
we prove in Section 14.2.5, ((mi:Ti iEI» is contractive in each Ti .
We call Gen ("generators") the set of functions on cupers of the form A(S)((mi:S-tTi
iEI)). Such a function can be written in the form A(S)((mi:S-tTi iEI)) uniquely:

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 .

14.2.3 Inclusion and Subtyping


Our intent is to interpret subtyping as inclusion of cupers. We now consider some of
the features of our semantic definitions of the previous section from the point of view
of subtyping.
The properties of Univ, -t, n, and U are the expected ones. Specifically, Univ is the
largest cuper without * in its domain; R-tT is contravariant in R and covariant in T;
niEI Ri is covariant in each Ri and contravariant in I; UiEI Ri is covariant in each R; and
in I. We have a less obvious inclusion property for fixpoints:
Proposition 14.2-2
Assume that F and G are contractive and that for every Rand T if R !;;; T then F(R)
!;;; G(T). Then J.1(S)F(S) !;;; J.1(S)G(S).
190 PART II. SECOND-ORDER CALCULI

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),

Pn(x'» E Gm(S) for almost all m; by completeness, (x,x') E J.I.(S)G(S).


o
Finally, let us consider object types. In the framework of the self-application
semantics, we may attempt to model object types as recursive record types, but then
we fail to obtain all the expected subtypings. Our semantic approach is based on that
attempt. Now, however, we have joins available, so we define an object type to be a join
of recursive record types. We make the join large enough to obtain all the expected sub-
typings: each object type is designed to contain all longer object types. For example, we
interpret the type Point as:
U {J.I.(S)F(S) I F E Gen, F ~ A(S){(x,y:S~Int))}

and the type ColorPoint as:


U {J.I.(S)F(S) I F E Gen, F ~ A(S){(x,y: S~Int, c: S~Color»}

(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»}

and thus we obtain that ColorPoint is a subtype of Poin t.


More generally, we have the following property:
Proposition 14.2-3
If I ~ J, then ((m;:Tj jeI)) ~ ((m i:Ti i.I)).
Proof
All extensions of A(S)«mj:S-+ Tj jeI» are also extensions of A(S)«mi: S~ Ti i.,», hence
the result.
o
This proposition suffices for our purposes. We may already note, however, that we
will not be able to account for "structural subtyping" of object types. Recall that in Sec-
tion 13.1.3 we discussed an alternative rule for update:
(Val Structural Update) (where A == [/i:Bi i.I..,))
Ef--a:C Ef--C<:A E,x:Cf--b : Bj jEl..n
E f-- a . lj~c;(x : C)b : C
As we argued, this rule relies on the structural subtyping assumption that every sub-
type of an object type is an object type. This assumption does not hold in our model: it
14. A SEMANTICS 191

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.

14.2.4 Properties of Joins


In this section we obtain some necessary results about joins of relations. A join is
defined with a closure operation C. The definition does not give a very explicit descrip-
tion of the elements of the join. In particular, it is not true that if (x,y) E 5 u T then either
(x,y) E 5 or (x,y) E T, and the definition of 5 u T does not help much in pinning down
what else (x,y) could be.
Here we analyze the closure operation. We obtain results that enable us to reason
about all elements of a join by reasoning about the elements of the components of the
join. Some of the other results, though more technical, are also important in the next
section. The casual reader may wish to look only at Propositions 14.2-9 and 14.2-11.
We use some further notation for binary relations and sets of binary relations:
• po: The restriction of P to finite elements.
• T(P): The transitive closure of P.
• U(P): The completion of P: (x,y) E U(P) if and only if either (x,y) = (.1,.1) or (x,y)
= (Ui Xi,Ui Yi) for «Xi,Yi)) an increasing sequence in P.

• NUSR: The set of nonempty, uniform, symmetric binary relations.


• NUPER: The set of nonempty, uniform pers.
Proposition 14.2-4
If P E NUSR, then T(P) E NUPER.
Proof
We check that T(P) E NUSR, and that in addition T(P) is transitive.
• Transitive closure preserves nonemptiness .
• Transitive closure preserves symmetry .
• Transitive closure preserves uniformity: if (x,y) E T(P), then x = Zo P Zl . .. Zn-1 P Zn
= y for some zo, ... , Zn, hence p;(x) = Pi(ZO) P Pi(Zl) ... P;(Zn-1) P Pi(Zn) = Pi(y) for all i,
by the uniformity of P, so (Pi(X),Pi(y)) E T(P).
• Transitive closure establishes transitivity.
o
192 PART II. SECOND-ORDER CALCULI

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

E C(Q). Since f and g are continuous, (jtx),g(y)) = (Uifipi(X)),Ui g(Pi(y))). By com-


pleteness it follows that (jtx),g(y)) E C(Q).
o
The following proposition entails that, if Ri E CUPER for all i E I, and S E CUPER,
and f and g are continuous functions such that (x,y) E Ri implies (jtx),g(y)) E S, then (x,y)
E UiEI Ri implies (jtx),g(y)) E S.

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

14.2.5 Metric Properties


As mentioned above, Amadio has studied the metric properties of -7 and ~. In sum-
mary, -7 is contractive; if H(R1,. ..,Rk+l) is nonexpansive in R1, ... , Rk and contractive in
Rk+l, then ~(Rk+l)H(Rl" .. ,Rk+l) is nonexpansive in R1, ... , Rk; and if H(R1, ... ,Rk+l) is con-
tractive in R1, ... , Rk+l' then ~(Rk+l)H(Rl'" .,Rk+l) is contractive in R1, ... , Rk.
Now we can prove that the object-type construction is contractive, and that
bounded join and intersection are nonexpansive. As a preliminary step, we deal with
the record-type construction.
Proposition 14.2-13
If Gi(R1, ... ,Rk) is nonexpansive in R1, ... , Rk for all i E I, then ((mi:Gi(R 1, ... ,Rk) iEI» is
contractive in R1, ... , Rk.
14. A SEMANTICS 195

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

14.3 The Interpretation of Types and Typed Terms


Finally in this section we give an interpretation of types and terms, and check the
soundness of FOb<:J1 under this interpretation. We consider not only the typing rules
of FOb<:J1 but also its equational rules (given by the fragments l:!=, l:!=x, l:!= ..., l:!=Ob, l:!=<;,
l:!=<:Ob, l:!=<:", l:!.<:v, and l:!=<:3). The proofs are long but mostly straightforward because
of the previous semantic development.

14.3.1 Interpreting Types


We define the semantics function for types:
[ n (TV
: -7CUPER)-7(TE-7CUPER)
14. A SEMANTICS 197

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).

14.3.2 Interpreting Typed Terms


We define the semantics function for terms:

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.

(Sub Object) (Ii distinct)


E f- Bi ~iEl .. n+m

E f- [li:Bi ie1..n+m] <: [li:Bi iel ..n]

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.

15.1.1 Defining <;-Objects


We examine types of the form:
<;(X)[li:BdX+) iE l..n) where BdX+) indicates that each BdX) is covariant in X
We call these structures <;-object types, and <;-objects their corresponding values. The
parameter X in <;(X)[I;:BdX+) iEl..n) is intended as the type Self hypothesized in Section
9.6. We routinely call the parameter of a Self quantifier a Self type, or simply Self. The
Bi are result types of methods; they may depend on Self. If Self occurs in a Bi, it must
occur covariantIy there.

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
202 PART II. SECOND-ORDER CALCULI

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.

15.1.2 Derived Rules for C;-Objects


Building on the rules for the Self quantifier (Chapter 13), we consider special rules for
<;-objects. We rely on the following notation. We use the syntax wrap(Y<:A=C)b{ Y} from
Chapter 13 for <;-object values, where b now has an object type. Operations on <;-objects
are indicated by special syntax that encapsulates occurrences of the use construct:
<;-Object operations
I
Let A == <;(X)[li:BdX+) i€1..n], A(C) == [li:Bi«q i€1..n], and jEl..n.
aA,lj ~
use a as Z<:A, y:A(Z) in y.lj : BjiAB
a,lj==(Y<:A,y:A(Y)C;(x:A(y)b{Y,y,x} ~
use a as Z<:A, y:A(Z) in wrap(Y< :A=Z)(y.lj==C;(x:A(y)b{Y,y,x)): A

Essentially, aA,lj is a form of selection and a,lj==(Y<:A,y:A(Y)C;(x:A(y)b is a form of


update. In the selection operation, we include a subscript A in order to make the defi-
nition unambiguous; this subscript is a type for the term a, and is often omitted. In the
update operation, the updating method b can take advantage of three variables: (1)
Y<:A, the unknown subtype of A that was used to construct a; (2) y:A(y), the raw object
inside a, which can be thought of as the old self: y is the value of self at the time the
update takes place, containing the old version of method Ij; (3) x:A(y), the regular self
of the updating method b.
Combining the rules for objects and for Self quantification we derive the fol\owing
rules. (Some of the derivations are in Appendix C.3.)
15. DEFINABLE COVARIANT SELF TYPES 203

(Type C;Object) (Ii distinct) (Sub C;Object) (Ii distinct)


E, X I- BiIX+) ViE l..n E, X I- BiIX+) ViEl .. n+m
E I- C;(X)[h:Bi/X) iELnj E I- C;(X)[li:Bi/X} iE1..n+mj <: C;(X)[/i:BiIX} iE1..nj

(Val C;Object) (where A;: qX)[li:B;{X+) ;<1.."))


E I- C <: A E I-- bHC» : A(C)
E I- wrap(Y<:A=C)b/Y) : A

(Val C;Select) (where A;: qX)[I;:B;{X+/ ;.1..11))


E I-a: A jEl .. n
E I- aA,I; : B;HAD

(Val C;Update) (where A;: C;{X)[l;:B;{X+/ ;el.."))


E I- a : A E, Y<:A, y:A(Y), x :A(y) I- b : Bjt¥» jEl..n
E I- a,lj~(Y< : A,y:A(Y)s(x:A(Y)b: A

(Eq C;0bject) (where A;: C;(X)[l;:Bi{X+/ i.I..II), A ' ;: C;{X)[I;:B;{X+/ ;el..lI+m))

E I- C <: A' E I- biC» H b'K»: A'(C)


E I- wrap(Y<:A=C)bIY) H wrap(Y< :A'=C)b'/Y) : A

(Eq Sub C;0bject) (where A;: C;{X)[l;:B;{X+/ ;<1..11), A';: qX)[I;:B;{X+/ ;EI..II+m))

EI- C <: A' E, Xi:A(C) I- MC» : BilC» ViEl..n


E, xrA'(C) I- b;HC»: B;fC» VjEn+l..n+m
E I- wrap(Y<:A=C)[li= s(xi:A(y)bi/Y) iELn] H
wrap(Y<:A'=C)[li=s(xi:A'(y)bj/Y) iel..n+m] : A

(Eq C;Select) (where A;: qX)[i;:B;{X+) ;Et .. ,,))


EI-a Ha': A jEl .. n
E I- aA,I; H a'A,I; : B;IAD

(Eq C;Update) (where A ;: qX)[I;:B;{X+/ ;El.II))


E I- a H a': A E, Y<:A, y:A(Y), x:A(y) I- b H b': BjHYD jEl..n
E I- a,lj~(Y<:A,y:A(Y)s(x : A(Y)b H a',I;~(Y< : A,y:A(Y)s(x : A(Y)b': A
204 PART II. SECOND-ORDER CALCULI

(Eval ~Select) (where A == 9X)[li:B;!X+) iEl.n), C == wrap(Z<:A=C)a{Z))


E f- c : A jEl .. n

(Eval ~Update) (where A == ~(X)[li:Bi{X+) iEl.n), C == wrap(Z<:A=C)a{Z))


E f- c: A E, Y< :A, y:A(y), x:A(y) f- b{Y,y,x): Bj811 jE1..n
E f- c.lj~(Y<:A,y:A(Y)C;(x:A(Y)b{Y,y,xl H
wrap(Z<:A=C)a{Z).lj~C;(x:A(Z»b8Z,a{Z),x» :A

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

15.2 Examples, with Typing


We are now ready to reexamine some examples. We find that these examples can be
typed rather easily when seen in terms of C;-objects, even when a method needs to
return or to modify self.

15.2.1 Movable Points


This is the typed version of the example in Section 6.5.1. We begin by defining the types
of one-dimensional and two-dimensional movable points using the Self quantifier:
PI ~ C;(Self)[x:lnt, mv_x:lnt~Selfl
P2 ~ C;(Self)[x,y:lnt, mvJ,mv....Y:lnt~Self]

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'

r[ Ill, S:PI(PI), dx :lnt f- S.X := s.x+dx : P1(PI)


Ill, S:P1(P I), dx:lnt f- PI <: PI
Ill, S:P1(P1), dx:lnt f- wrap(P 1, s.x:= s.x+dx) : PI
by (Val Select), (Val Update)
by (Sub Refl)
(Val C;Object)
Ill, S:P1(P1) f- A(dx:lnt) wrap(P1, s.x := s.x+dx) : Int~PI (Val Fun)
III f- [x = 0, mv_x = <;(S:P1(P1)) A(dx:lnt) wrap(h s.x := s.x+dx)] : P1(P1) (Val Object)
III f- PI <: PI by (Sub Refl)
III f- wrap(Sel!=P1)[x = 0, mv_x = <;(s:PI(Self)) A(dx:lnt) wrap(Self, s.x := s.x+dx)] : PI
(Val C;Object)
The rule (Val C;Select) allows us to invoke methods whose type involves Self
originlomv_x : Int~PI
Moreover, the equational theory allows us to derive expected equivalences, such as:
originlomvJ(I) H
wrap(Sel!=PI)[x = I, mv_x = <;(s:PI(Sel!)) A(dx:lnt) wrap(Self, s.x := s.x+dx)] : PI
that is, the unit point equals the result of moving the origin point.
206 PART II. SECOND-ORDER CALCULI

15.2.2 Backup Methods


This is the typed version of the example in Section 6.5.2. The basic type in question is:
Bk g, C;(SelJ)[retrieve:Self, backup:Self)
To show a less trivial example, we define movable points with backup and retrieve
methods:
BkPj g, C;(SelJ)[retrieve:Self, backup:Self, x:lnt, mv_x:lnt~Self)

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.4 Object-Oriented Natural Numbers


This is the typed version of the example in Section 6.5.3. The type of natural numbers
is quite interesting: it contains a universal quantification of the form Ii(Z) Z~(X~Z)
~Z, with a free X in covariant position.

NOb g, C;(Self)[succ:Self, case:Ii(Z) Z~(Self~ZhZ)


15. DEFINABLE COVARIANT SELF TYPES 207

The zero numeral can then be typed as follows:


zeroQb : N Qb £
wrap(Self=NQb)
[case = A(Z) A(z:Z) A(fSelf~Z) z,
succ = c,(n:Nob(Self»
wrap(Self, n.case := A(Z) A(z:Z) A(fSelf~Z) j(wrap(Self, n»)J
The operations are:
succ : NQb~Nob £ A(n:Nob) n.succ
pred: NOb~Nob £ A(n:Nob) n,case(Nob)(zeroQb)(A(p:N ob ) p)
iszero : NOb~Bool £ A(n:NQb) n.case(Bool)(true)(A(p:NQb)false)
Looking back at Section 9.4, we can now explain the correspondence between our
second-order typing for numerals and the typing based on sums. The type for the case
method, Unit+Self, can be encoded as 'V(Z)Z~(Self~Z)~Z, since in general A+B can be
encoded as 'V(Z)(A~Z)~(B~Z)~Z . Using this encoding, we can view the definition
of Section 9.4 as a variant of the one given here.

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

15.2.6 Storage Cells


This is the typed version of the example in Section 6.5.5. The types for cells and restor-
able cells are:
Cell g, r;(Self)[contents:Nat, get:Nat, set: Nat~Selfl
ReCell g, r;(Se/f)[contents:Nat, get:Nat, set:Nat~Self, backup:Nat, restore:Se/fJ
ReCe/l' g, r;(Self)[contents:Nat, get:Nat, set:Nat~Se/f, restore:Selfl
where ReCell <: ReCel/' <: Cell.
The code for cells and restorable cells is typed as follows:
myCell : Cell g,
wrap( Self=Cell)
[contents = 0,
get = r;(s:Cell(Self») s.contents,
set = r;(s:Cell(Se/f)) 'A.(n:Nat) wrap(Self, s.contents := n)J
myReCell : ReCell g,
wrap(Se/f=ReCell)
[contents = 0,
get = r;(s:ReCell(Se/f)) s.contents,
set = r;(s:ReCell(Self)) 'A.(n:Nat)
wrap(Self, (s.backup := s.contents).contents := n),
backup = 0,
restore = r;(s:ReCell(Se/f)) wrap(Self, s.contents := s.backup)J
myOtherReCell : ReCe/l' g,
wrap(Se/f=ReCe/l')
[contents = 0,
get = r;(s:ReCe/l'(Se/f)) s.contents,
set = r;(s:ReCell'(Self)) 'A.(n:Nat)
wrap(Se/f,
(s .restore == r;(z:ReCell'(Se/f)) wrap(Self, z.contents := s.contents»)
.contents := n),
restore = r;(s:ReCell'(Se/f)) wrap(Self, s.contents := 0)]

15.3 Binary Methods and the Covariance Requirement


A typical example of a binary method is an equality method in a point object. Such a
method has a parameter of the same type as its self variable, so we might naturally
assign it the type Self [36J. We could write points with equality as follows :
P1eq g, r;(Se/f)[x:lnt, mv_x:lnt~Self, eq : Self~BooIJ
15. DEFINABLE COVARIANT SELF TYPES 209

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.

15.4 Classes and Inheritance, with Self


We now extend the techniques developed in Section 8.5 to model classes and inherit-
ance. We take into account Self, and the need for pre-methods and classes to be suffi-
ciently polymorphic.
210 PART II. SECOND-ORDER CALCULI

If A;: <;(Self)[li:BdSeirl iE1..nj is an object type, then:


Class(A) ~ [new:A, li: V(Self<:A)A(Self)~Bi{Selfl iE1..nj
can be seen as a class type. This is the type of a collection of pre-methods that can be
used in objects of type A and reused in other classes. Each pre-method is required to
work for an arbitrary subtype Self of A.
As before, A' <: A does not imply Class(A') <: Class(A). Hence we say:
Class(A') may inherit from Class(A) iff A ' <: A
If Class(A ') may inherit from Class(A), then A' and A have the forms A' ;:
<;(Self)[li: Bi{Selrl iE1..n+mj and A;: <;(Self)[li:Bi{Self+1 iE1..nj. Thus, since A' <: A, we obtain
V(Self<:A)A(Self)~BdSelfl <: V(Self< : A')A '(Self)~BdSelfl for iEl..n. Hence pre-meth-
ods of members of Class(A) may be reused for members of Class(A'). Note that this
reuse property would not hold if we took A~Bi«A» and A'~Bi(A ') as the types of the
pre-methods. The use of polymorphic types for pre-methods is essential.
For a class of type Class(A), we can write the body of the method new as follows:
~(z: Class(A) )wrap( Self=A)[ Ij=~(s :A( Self ))z.lj(Self )(s) iE !..nj
There remains a serious problem in defining classes: writing pre-methods of types
For example, for the type of points PI from Section 15.2.1,
V(Self< : A)A(Self)~BdSelfl .
we would have to write pre-methods for the class type:
Class(P I ) ~
[new:P!,
x:V(Self<:PI ) PI(Self)~lnt,
mv_x:V(Self<:PI ) PI(Self)~lnt~Selfj
Writing a pre-method of type V(Selj<:PI ) PI(Self)~Int is easy, but writing a useful pre-
method of type V(Self<:P I) PI(Self)~Int~Selfis impossible in the present system. Not
even a pre-method that returns self has this type. This difficulty is related to the prob-
lem of updating methods that return self, discussed next. Solutions are described in
Sections 15.6 and 16.6.

15.5 Updating from the Outside


In Section 15.1 we remark that it is easy to create a <;-object, because its initial methods
need to work only for the actual type of the object being constructed. In particular,
methods that update self present no difficulties; see the examples of Section 15.2.
In contrast, updating methods from the outside is challenging. If we want to
update a method of an existing <;-object c of type A, the new method must work for any
possible subtype B of A, because c might have been built as an element of B. We know
neither the "true type" of c, nor the "true type" of the self parameters of its methods.
When updating a method of c, the updating method can assume only that the object
has been constructed from an unknown subtype of A.
15. DEFINABLE COVARIANT SELF TYPES 211

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

r20p (SeIJ<:R2J x':R2(Se1fl) ~(x:R2(SeIJ)) wrap(Self, X)


~
= wrap(Y<:R2=R j )[p = ~(X:R2(Y» wrap(Y, X), q = ~(S:R2(Y» s.pot) (unsound)
r20p ~ (SelJ<:R2J X':R2(Self)) ~(X :R2(Selfl) wrap(Self, Xl
= wrap(Y<:R2=R j )
[p = wrap(Y, [p = rj, q = ~(S :R2(Y» s.p.t), q = ~(S:R2(Y» s.p.t) (unsound)
Unsound behavior can be observed by invoking q after either of these two updates,
because the field t is missing from s.p.
The reason for this unsound behavior can be traced back to the rule for construct-
ing C;-objects. Because of the flexibility we have in constructing C;-objects out of proper
subtypes of themselves, the parameters X and x' at our disposal when updating may be
shorter than the subtype used originally when constructing the object. For example, r2
is constructed with a component p that is longer than the body of r2J to which x or x'
would be bound. Therefore returning x or x' would not be safe because they would be
too short.
In conclusion, we discover that the rule (Val C;Update), although very powerful for
updating simple methods and fields, is not sufficient to allow us to update methods
that return a value of type Self. We are in the strange but apparently inevitable position
of being able to construct a point with a mv_x method, but unable then to update mv_x
with an identical method. One solution to this problem is discussed in the next section.

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.

15.6.1 The Recoup Method


Let us consider the type:
R g, C;(SelJ)[r:SelJ)
We can build an element of R as follows:

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

15.6.2 The Recoup Invariant


The technique just described gives the correct result only as long as recoup is bound to
C;(s:A(Se/f))wrap(Self,s). Otherwise the operational behavior is not the expected one. We
214 PART II. SECOND-ORDER CALCULI

must assume that recoup is bound to r;(s:A(Se/f)wrap(Self,s) as an invariant. We have no


elegant way of enforcing this invariant because recoup is a component like any other.
Our ability to reason about programs is dependent on the recoup invariant. In the
following example, we use the recoup invariant to argue that the result of updating an
object from the outside is equivalent to the Original object. We use the object a from Sec-
tion 15.6.1:
a,backup ~ (Self<:Bk, s':Bk(Se/f)) r;(s:Bk(Se/f)) (s.retrieve:= s.recoup).recoup
wrap(Self=Bk) b.backup ~ r;(s:Bk(Self)) (s .retrieve := s.recoup).recoup
wrap(Se/f=Bk) b.backup ~ r;(s:Bk(Self) (s .retrieve := wrap(Self, s».recoup
(since, by the recoup invariant, we have s.recoup = wrap(Self, s»
wrap(Se/f=Bk) b.backup ~ r;(s:Bk(Self)) wrap(Self, s.retrieve:= wrap(Self, s»
(since, by the recoup invariant, we have
(s.retrieve:= wrap(Self, s».recoup = wrap(Self, s.retrieve := wrap(Self, s»)
a
The correctness of typing, on the other hand, does not depend on the recoup invariant.
An invariant of this kind may be acceptable for a programming language: recoup
would be a distinguished component that is appropriately initialized and that cannot
be updated. Even without language support, we may be disciplined enough to pre-
serve the recoup invariant and thus we may solve the problem of updating methods
with result type Self. If we initialize recoup correctly and do not update it, then the oper-
ational semantics works out as expected. However, it may be hard to incorporate the
recoup invariant in an equational theory for the programming language.

15.6.3 The Recoup Method and Classes


If a type A has the form ~(Se/f)[recoup:Self, ... J, then we can write useful polymorphic
functions of type:
If( Self<:A )A( Self )~Se/f
that are not available without recoup, for example:
f: If(Se/f<:Bk) Bk(Se1fHSe/f ~
A(Self<:Bk) "A(s:Bk(Self) (s .retrieve := s.recoup).recoup
Such functions are parametric enough to be used in updates from the outside, as in:
a,retrieve ~ (Self<:Bk, s':Bk(Self)) r;(s:Bk(Self) f(SelJ)(s)
and can also be used for defining classes. Extending the techniques of Section 15.4, we
can take advantage of recoup to write a class for points and, in particular, we can write
a pre-method for mv_x:
PI ~ C;(Se/f)[recoup:Self, x:lnt, mv_x : lnt~Se/f)
15. DEFINABLE COVARIANT SELF TYPES 215

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.

15.7 Objects with Structural Invariants


So far we have worked with type and equational theories that are valid in standard
denotational models, with a standard interpretation of subtyping (see Chapter 14).
However, if we imagine a programming language including our objects, we see that
additional important properties may be satisfied by the operational semantics of the
language that are not satisfied in standard models. These additional properties give us
an alternative way to handle methods that produce results of type Self.
In this section we provide plausibility arguments as to why these properties may
be operationally sound, based on our current understanding. In Chapter 16 we incor-
porate these properties into rules, and provide an operational semantics and a proof of
subject reduction for these rules.

15.7.1 Towards Structural Rules


The additional properties we wish to capture are:
• A structural sub typing invariant.
• An object shape invariant.
Informally, the structural subtyping invariant is that every subtype of an object type is
a longer object type. The object shape invariant is that all elements of an object type
have a certain shape. An example of a shape invariant, from Section 15.6, is that all
objects have a recoup method of a given form. In this section we study a different shape
invariant, along with its typing and equational consequences.
216 PART II. SECOND-ORDER CALCULI

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.

15.7.2 Structural Objects


We define a new syntax for object types, to indicate those types satisfying the structural
subtyping invariant. We also define new syntax for objects, to indicate those objects
satisfying a particular shape invariant. We give new syntax for object operations as
well; the new operations take advantage of the shape invariant.
Obj(X)[lj:BdX} jE1..n] structural object type
obj(X=C)[lj=C;(xj:X)bdX,xj} jELn] structural object
a,lj structural method invocation
a,lj~(Y<:A,y: Y)C;(x: Y)b( Y,y,x} structural method update
We define:
For C == Obj(X)[lj:BdX} jELn], a == obj(X=C)[l;=c;(xj:X)bdX,x;} jE1..n], and fresh x;':
C(D) ~ [lj:BjIDH jE1..n]
a(D) ~ [lj=C;(x;':C(D))bj{D,wrap(D,xj')H jE1..n]
Our intended interpretation of this new syntax is in terms of ~-objects:
Obj(X)[lj:BdX} jELn] ~ ~(X)[lj: BdX+ } jE1..n]

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

a.lj~(Y< : A,y:Y)~(x : Y)bIY,y,x} ~


wrap(Y<:C=C)a(Y).lj~~(x ':C(Y)b«Y,a,wrap( Y,x')>>
for C =Obj(X)[/i: BiIX} 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).

15.7.3 Rules with Preliminary Structural Assumptions


Given these definitions, we can derive the following rules. We use the fact that if
obj(X=C)[li=~(xi:X)bi if1..n) has some type A, then it also has type C, and C is a subtype
ofA.

Objects, with preliminary structural assumptions


(Type Object' )
E, XI- BiIX+} ViEl ..n .
E I- Obj(X)[li:B;\X} ifL")

(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")

(Val Object') (where A", Obj(X)[li:Bi{XI i<I..,,])

E, Xi:A f- b;{A j : Bi(Aj 'lfiEl..n


E f- obj(X=A)[li=~(xi:X)biIX} ifL") : A

(Val Select') (where A", Obj(X)[li:Bi{XI iEi..nl, a '" obj(X=C)[li=~(xi:X)bi{X,xil i<l..n+m])


Ef-a:A jEl..n
E f- a.l j : BjiA»
218 PART II. SECOND-ORDER CALCULI

(Val Update') (where A == Obj(X)[li:Bi(X} i<l..n], a == obj(X=C)[li=~(xi: X)bilX,xi} i<l..n+m])


Ef-a:A E, Y< :A,y:Y,x:Yf-b:Bj(Yj jEl..n
E f- a.lj~(Y< : A,y: Y)<;(x:Y)b : A

(Eval Select') (where A:; Obj(X)[li:Bi(X} i<l..n], a == obj(X=C)[I,=~(xi:X)biIX,xi} i<l..lI+m])


Ef-a : A jEl..n

(Eval Update') (where A :; Obj(X)[li:Bi(X} i<I..II], a == obj(X=C)[li=C;(Xi:X)bi(X,Xi} i<l..lI+m])


E f- a: A E, Y< :A, y:Y, x :Y f- b{Y,y,xl : BjfYj jEl..n
E f- a .lj~(Y<:A,y:Y)<;(x: Y)b{Y,y,xl H
obj(X=C)[lj=<;(x:X)b{X,a,xB, IF<;(xj:X)bj{X,xjl iE(1..n+m Hj1 j : A

The shape invariant is established by (Val Object'), assuming it is true of subcompo-


nents. The invariant is assumed explicitly by (Val Select' ) for a particular term; and pre-
served if already true of subcomponents. The invariant is also assumed explicitly by
(Val Update'), and preserved for the updated object and its subcomponents. The
invariant is preserved by reduction, as seen in (Eval Select') and (Eval Update').

15.7.4 Rules with Structural Assumptions


So far we have dealt only with the shape invariant. To exploit the structural subtyping
invariant, we consider the following version of the rules, which is trivially derivable
from the previous one.
Objects, with structural assumptions
(Val Select") (where A:; Obj(X)[li:BiIX} i<l..n], A ' :; Obj(X)[/i:BiI X } i<I..II'],
a:; obj(X=C)[!;=C;(Xi:X)bi(X,Xi} i<l..n+m])
Ef-a : A Ef-A<: A' jEl..n '
E f- a.li : Bi(Aj

(Val Update") (where A:; Obj(X)[li:Bi(X} i<I..II], A ' :; Obj(X)[li:B,IX} i<I..II'],


a :; obj(X=C)[li=~(Xi: X)biIX,Xi} i<l..n+m])
E f- a: A E f- A <: A' E, Y< :A, y :Y, x :Y f- b : Bi{Y} jEl..n'
E f- a.li~(Y<:A,y:Y)<;(x:Y)b : A

(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

E f- a.li H biK,a} : BiM»


15. DEFINABLE COVARIANT SELF TYPES 219

(Eval Update") (where A == Obj(X)[li:Bi{X} idn], A' '" Obj(X)[h:Bi{X} i<l..n'],


a'" obj(X=C)[li=C;(xi:X)bilX,xi} i<l..n+m])
E I- a: A E I- A <: A ' E, Y< :A, y:Y, x:Y I- b{Y,y,xl : Bjgn jE1..n
E I- a.lj~(Y< :A,y : Y)~(x:Y)b{Y,y,xl H
obj(X=C)[lj=~(x: X)b«X,a,x», li=~(xi:X)b;{X,xi} idLn+mHj'j : A

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

Unfortunately, this stronger rule is not derivable. It is, however, reasonable


because the structural invariants say that (at run-time) a and A can be only of the form
given in (Val Select"). Moreover, as we show in Chapter 16, rules such as this one
would solve the difficulties that we have found in constructing pre-methods for classes
and in updating methods from the outside.
We cannot proceed further in our exploration of structural rules within Ob<:j1' In
Chapter 16, we develop an alternative type system. There we remove the side condi-
tions about the form of a subtype of an object type, and the side conditions about the
shape of an object (except for evaluation rules). The removal of the side conditions has
the following effects: (1) where a structural object was previously required, we allow a
variable of a certain object type to occur; (2) where an object type with a certain shape
and bound was previously required, we allow a type variable with the same bound to
occur.
16 PRIMITIVE COVARIANT SELF TYPES

In Chapter 15 we have investigated an encoding of Self obtained via standard construc-


tions. We have derived typing rules and equational theories for objects with Self that,
as shown in Chapter 14, are sound in a standard denotational model. We have also
devised the recoup technique to remedy a limitation of the derived typing rules, and
discovered some desirable typing rules that are not derivable.
Building on that experience, we now axiomatize Self directly, taking Self as prim-
itive. The new rules are similar to the ones in Section 15.7, and allow us to update meth-
ods with result type Self. These rules are not syntactically derivable, and are not sound
for standard interpretations of subtyping as inclusion. Still, they are more natural (per-
haps even required) from the point of view of programming. In absence of models, we
establish their consistency by means of an operational semantics and a subject reduc-
tion theorem. We do not consider equational theories beyond those implicit in reduc-
tion systems.
As an illustration of the use of Self, we revisit some of our standard examples.
Extrapolating from the examples, we exploit Self and bounded universal quantifiers in
a new representation for classes and inheritance.

16.1 Primitive Self Types and Structural Rules


The most remarkable new aspect of the type system presented in this chapter is that it
is based on structural assumptions about the universe of types. These assumptions are
operationally valid, but would not hold in natural semantic models.
Rules based on structural assumptions (structural rules, for short) are not peculiar
to object types. For example, the following rule for pairing enables us to mix two pairs
a and b of type C into a new pair of the same type. The only assumption on C is that it
is a subtype of a product type BjxB2.

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}.

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
222 PART II. SECOND-ORDER CALCULI

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.

16.2 Objects with Structural Rules


In this section, we define a minimal object calculus with Self and variance annotations;
we name it S. Later we add quantification to S and we prove a subject reduction theo-
rem for the extended system.

16.2.1 Syntax of Types and Variance


In order to obtain a flexible type system, we need constructions that provide both cova-
riance and contravariance. For example, both variances are necessary to define func-
tion types. There are several possible choices at this point. One choice would be to take
invariant object types plus the two bounded second-order quantifiers, and to proceed
as in Chapter 15. Instead, we prefer to embed variance annotations directly into object
types. This choice is technically sensible because it increases expressiveness, delays the
need to use quantifiers, and yet does not significantly complicate the operational
semantics and the related proofs.
We consider object types with Self of the form :
16. PRIMITIVE COVARIANT SELF TYPES 223

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, +})

Formally, B{X+} indicates that X occurs at most positively in B; similarly, B{X-}


indicates that X occurs at most negatively in B.
Variant occurrences
Y{X+} whether X = Yor X ~ Y
Top{X+) always
Obj(Y)[liUi:Bi iel..n]{x+} if X = Y or for all iE l..n:
if W= +, then B;/X+}
if Vi= -, then Bi{X-}
if Vi= 0, then X~FV(Bi)
Y{X-} ifX~Y
Top{X-} always
Obj(Y)[l;tJi:Bi ie l..n]{x- } if X = Yor for all iEl .. n:
if Vi= +, then Bi{X-}
if Vi= -, then B;/X+}
if Vi= 0, then X~FV(Bi)
if neither A{X+} nor A{X-}

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

16.2.2 Syntax of Terms and Operational Semantics


We introduce a new syntax for objects and object operations, inspired by the consider-
ations of Section 15.7 about structural invariants.
Syntax of S terms
a,b ::= terms
x variable
obj(X=A)[li=~(xi:X)bi iE1.."] object (Ii distinct)
a.l method invocation
a.I;:(Y< :A,y: Y)~(x : Y)b method update

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

(Red Select) (where v ' =obj(X=A)[li=<;(xj:X)bi(X,xjl iELn))

f- a -- v' f- bj!A,v'» "... v jE l..n


f- a.lj - v
16. PRIMITIVE COVARIANT SELF TYPES 225

(Red Update) (where v '" obj(X=A)[lF~(Xi:X)bi iE1.."])


~a~v jEl..n

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.

16.2.3 Judgments and Rules for Types


The judgments used in the typing rules are familiar ones:
Judgments
E~o well-formed environment judgment
E~A type judgment
E~ A<: B subtyping judgment
E ~ vA <: v'B subtyping judgment with variance
E~a : A value typing judgment

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)

!1l~0 E,x : A~ 0 E, X< :A f- 0

(Type X<:) (Type Top) (Type Object) (Ii distinct, UiE{O,-:J)


E', X< :A, En ~ 0 E~ 0 E, X<:Top ~ B;\X+) '\tiEl..n
E', X< :A, En ~ X E ~ Top E ~ Obj(X)[lj\!j:B;\X) iEI."]

(Sub Refl) (Sub Trans) (Sub Top) (Sub X)


E~A E ~ A <: B E ~ B <: C E~A E', X<:A, En ~ 0

E~A< : A E~A<: C E~A< : Top E', X<:A, En ~ X <: A

(Sub Object) (where A'" Obj(X)[l,Vi:B;/Xj iEI.."+m ), A' '" Obj(X)[l,Vi': Bi' {Xj iEI.."])

E~ A E ~ A' E, Y< :A ~ Vj Bji¥H <: vi' B;'{Yj '\tiE l..n


E~A <: A '
226 PART II. SECOND-ORDER CALCULI

(Sub Invariant) (Sub Covariant) (Sub Contravariant)


Ef-B E f- B <: B' VE {O,+} E f- B' <: B VE {a, -}
E f- °B <: °B E I- vB <: + B' E I- v B <: - B'

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]

An analogous property fails with /.l instead of Obj.

16.2.4 Rules for Terms


The rules for term typing are suggested by the ones in Section 15.7.
Terms with typing annotations
(VaISubsumption) (Val x)
E f- a : A E f- A < : B E', x:A, E" f- <>
Ef-a : B E', x:A, E" f- x : A

(Val Object) (where A'" Obj(X)[I,vj:Bj{Xj iEl.n))

E, Xi:A f- b;(AD : BjKAB 'liiEl..n


E f- obj(X=A)[lj=<;(xj: X)bj{X} iE1..n] : A

(Val Select) (where A' '" Obj(X)[I,Vi:Bi{Xj iEl.n))

Ef-a:A E f- A <: A' ViE {o,+} jE l..n


16. PRIMITNE COVARIANT SELF TYPES 227

(Val Update) (where A':; Obj(X)[I,'Ui:B;\X) iEl.n])

El-a:A EI-A< : A' E,Y<:A,y:Y,x:Yl-b:Bj«n 'UjE{O,-) jE1..n


E I- a.lj#o(Y<:A,y: Y)~(x: Y)b : A

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])

E f- a : A 'UjE {O,+) jE l..n


E I- a.lj : BjM»

(Val Non-Structural Update) (where A:; Obj(X)[I,'Ui:Bi{X) iEI."])


E I- a: A E, Y<:A, y:Y, x:Y I- b : Bj«n 'UjE{O,-) jEl..n
E I- a.lj#o(Y<:A,y: Y)~(x: Y)b : A

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

The type rules for bounded universal quantifiers are:


Quantifier rules
(Type Alk:) (Sub All)
E, X <:A f- B Ef-A '<: A E , X<:A ' f-B<:B'
E f- \f(X<:A)B E f- \f(X<:A)B <: \f(X<:A ' )B'

(Val Fun2<: ) (Val AppI2<: )


E, X < :A f- b : B E f- b : \f(X< :A)B{Xj E f- A' <: A
E f- A(X<:A)b : \f(X<:A)B E f- b(A') : BM'»

We also extend the definition of positive and negative occurrences:


16. PRIMITIVE COVARIANT SELF TYPES 229

Variant occurrences for quantifiers


(V(Y<:A)B){X+) if X = Y or both A{X-} and B{X+}
(V(Y<:A)B){X-) if X = Y or both A{X+} and B{X-}

16.4 Subject Reduction


The consistency of the operational semantics of Sit with its typing rules is established
by a subject reduction theorem. We start by stating a bound weakening lemma, a sub-
stitution lemma (both without praoO, three simple lemmas about subtyping, and a
lemma about variance. The main theorem then follows.
Lemma 16.4-1 (Bound weakening)
If E', X< :A, E" f- g and E' f- A' <: A, then E', X<:A', E" f- g.
o
Lemma 16.4-2 (Substitution)
(1) If E', X<:A, E"{X} f- g{X} and E' f- A' <: A, then E', E"iA'» f- g«A'l
(2) If E', x:A, E" f- g{x} and E' f- a : A, then E', E" f- g«aa.
o
The next three lemmas analyze the possible forms of a type C that is a supertype
of a given type. If the given type is Top, then C is Top. If the given type is an object type
C, then either C is Top or C is itself an object type, and in the latter case the component
types of C are subtypes or supertypes of the corresponding component types of C.
Similarly, if the given type is a bounded universal type, then either C is Top or C is itself
a bounded universal type.
Lemma 16.4-3
If E f- Top <: C, then C == Top.
Proof
By induction on the derivation of E f- Top <: C.
The (Sub Refl) and (Sub Top) cases are immediate. The (Sub X), (Sub Object), and
(Sub AII<:) cases are vacuous. For (Sub Trans) we have E I- Top <: C and E f- C <:
C. By induction hypothesis, C == Top. Then again by induction hypothesis, C == Top.
o
Lemma 16.4-4
If C' == Obj(X)[/,'lJ/:B;,{X} iEI] and E f- C <: C, then either C == Top,
or C == Obj(X)[I,wBdX} iEl) with J ~ I, and, for JEJ we have:
(1) if VjE {o,+}, then v;' E {o,+} and E, X<:C f- B;'{X} <: Bj{X},
(2) if VjE {O,-}, then V/E{O,-} and E, X<:C f- Bj{X} < : B/{X}.
230 PART II. SECOND-ORDER CALCULI

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

Case (Red Update) (where v == obj(X=C)[lj=c,(Xj:X)bj jE1.,,])


f- c v+ v jEl..n
f- c.lj;,(y<:c,y:Y)c,(x:Y)b{Y,y} v+ obj(X=C)[lj=c,(x:X)b{X,vj, Ij=c,(Xj:X)bj jE1.n-V'J

By hypothesis jIl f- c.lj;,(Y<:C,y:Y)c,(x:Y)b{Y,y,x} : A. This must have come from (1)


an application of (Val Update) with assumptions jIl f- c : C, and jIl f- C <: D where D
has the form Obj(X)[l,""j: Bj{X}, ... J, and jIl, Y<:C, y:Y, x:Y f- b{Y,y,x} : Bj{Y&, and
VjE {O,-}, and with conclusion jIl f- c.lj;'(Y<:C,y:Y)c,(x :Y)b{Y,y,x} : C, followed by (2) a
number of subsumption steps implying jIl f- C < :A by transitivity.
By induction hypothesis, since jIl f- c : C and f- c v+ v, we have jIl f- v : C.
Now jIl f- v : C must have come from (1) an application of (Val Object) with assump-
tions jIl, Xj:C f- MC,xji : B/(Ci and C == Obj(X)[I/v/:B/{X} jEl .. nJ, and with conclu-
sion fIl f- v : C, followed by (2) a number of subsumption steps implying fIl f- C <: C
by transitivity. By transitivity, jIl f- C <: D. Hence by Lemma 16.4-4 we must have
V/E{O,-}, and jIl, X<:C f- Bj{X} <: B/{X}, and by Lemma 16.4-2 fIl f- BjlCi <: B/ICT
From fIl, Y<:C, y:Y, x:Y f- blY,y,x} : BjlYi by Lemma 16.4-2 we get fIl, x:C f- blC',v,xi
: BjK'&, and by subsumption jIl, x:C f- blC',v,xi : B/ICT Then, by (Val Object), we
obtain fIl f- obj(X=C)[lj=c,(x:X)bIX,v,41j=c,(xj:X)bj jE1.n-1j'J : C'. Since jIl f- C <: A by
transitivity, we have fIl f- obj(X=C')[lj=c,(x:X)bIX,v,4 Ij=c,(xj:X)bj jE1.n-V'J : A by sub-
sumption.
Case (Red Fun2) (where v == A(X<:D)b)

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

implying ~ I- V(X<:D')C{X} <: V(X<:D)C{X}. Hence ~ I- 0 < : 0 and~,


' X<:D I- C{X}
<: ClX} by Lemma 16.4-5.
We have ~ I- 0" <: 0, and also ~ I- 0" <: 0' by transitivity. Therefore ~ I- C«D"R <:
QD"R, and ~ I- c«D"R : C«D"R by Lemma 16.4-2.
By induction hypothesis, since ~ I- dD"R : cgD "Rand I- c(D"R -- v, we have ~ I- v:
C«D"l By transitivity and subsumption, ¢ I- v: A.
o

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.

16.5.1 Storage Cells


Our first example illustrates some critical uses of the structural assumptions in (Val
Update). We define the type of storage cells with a get field and with a set method that,
given a number, returns a storage cell storing that number.
St ~ Obj(X)[get:Nat, set:Nat~X)

st: St ~ [get = 0, set = <;(x:St) 'A(n:Nat) x.get := n)


[get = <;(x:St) 0,
set = <;(x:St) 'A(n:Nat) x.get ;, (Y<: St, y :Y) <;(z:Y) n)
The claimed typing for the storage cell st can be derived as follows, assuming intro-
duction rules for Nat and -7. For compactness, all type annotations are omitted in terms
appearing in derivations.
234 PART II. SECOND-ORDER CALCULI

111, x:St ~ 0 : Nat Nat introduction

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.

16.5.2 Backup Methods


An interesting difficulty arises when trying to update fields of type Self; this difficulty
is avoided by using the old-self parameter of the update operator. Consider again
objects with backup:
Bk ~ Obj(Self)[retrieve:Self, backup:Self, ... J
0: Bk ~
obj(Self=Bk)
[retrieve = ~(s:Self) s,
backup = ~(s:Self) s.retrieve;. (Y<:Self, y:Y) ~(x:Y) y,
.. .J
Note the way the backup method is written. We might have expected a simpler coding
of this method, namely:
backup = ~(s:Self) s.retrieve := s,
These two definitions of backup are in fact operationally equivalent, because during
reduction the parameter y is replaced by the old self, s. The simpler definition of backup,
however, does not typecheck, as can be understood by verifying the typing of the cor-
rect version.
The crucial point in the derivation of 0: Bk is that the rule (Val Update) requires the
type Y for the body of the backup method. The parameter y has this type, but s does not.
The derivation is as follows:
236 PART II. SECON D-ORDER CALCULI

.. (retrieve, and other attributes)

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)

16.5.3 Object-Oriented Natural Numbers


We adapt the example of the object-oriented natural numbers to our new object types.
Compare this formulation with the one in Section 15.2.4, and note the absence of coer-
cions. The structural subtyping assumptions in our new rules are responsible for the
code simplification. Note, however, that the code of the suee method needs to use the
old-self parameter:
Nab £ Obj(Self)[suec:Self ease:'<i(Z) Z4(Self4Z)~Z]
zeroOb : Nab £
obj(Se/f=Nob)
[case = A(Z) A(Z:Z) ActSelf4Z) z,
suee = <;(n:SelJ)
n.ease ~ (Y<:Self y: Y) <;(x: Y) A(Z) A(Z:Z) Act Y~Z) fly)]
As shown in Section 16.5.1, we can now update even methods that return values of type
Self, given a sufficiently parametric new method. For example, we can typecheck:
zeroOb·suCC
~ (Self<:Nob, n':SelJ) <;(n:SelJ)
n.case ~ (Y<:Self, y: Y) <;(x: y) A.(Z) A.(z:Z) A.ctSelf~Z) fly)
Nab
The crucial intermediate step is to obtain the typing n.case ~ ... : Self.

16.5.4 Movable Points


We define types of movable points PI and P'b with the subtyping P 2 <: PI. A polymor-
phic function inc_x is used for building origin points origin I and origin2 of PI and P2. We
can view origin2 as inheriting the definition of inc_x originally intended for originl. The
code of mvJ is identical in the two origin points although Self refers to different types.
PI £ Obj(SelJ)[x:lnt, mv_x:lnt~Selfl
inc_x: '<i(Se1f<:PI) Self 4lnt~ Self £
A.(Self<:PI ) A.(s:SelJ) A.(dx:lnt) s.x := s.x+dx
originl £ obj(Self=Pj)[x = 0, mv_x = <;(s:Self) inc_x(SelJ)(s)]
16. PRIMITNE COVARIANT SELF TYPES 237

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 Classes and Inheritance, with Primitive Self


In this section we develop a presentation of classes based on types of the form
'tI(X<:A)X-7B/X}. We obtain a satisfactory conclusion to the encoding of classes for
object types with Selt first attempted in Section 15.4.

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

cannot be inherited into Class(A') == [new:A', I:'v'(X<:A')X--.?Nat), because it would pro-


duce a bad result. However, a class c' : CIass(N) can include a different method for 1
with result type Nat; this corresponds to method specialization on override, discussed
in the Review.
In summary, covariant components induce mild restrictions in subclassing. Invari-
ant and contravariant components induce no restrictions. In practice, inheritability is
expected between a class type C and another class type C obtained as an extension of
C (so that C and C have identical common components). In this case, inheritability triv-
ially holds for components of any variance.

16.6.2 Variations on Class Types


The variations on class types explored in Section 8.5.2 apply to the more general class
types studied here. For an object type A == Obj(X)[liVi:B;/XI iel) with methods Ii iel we
consider a restricted instance interface, determined by a set Ins!:;;; I, and a restricted sub-
class interface, determined by a set Sub!;; I. We define, for Ins,Sub ~ I:
Ciass(A)lns,Sub t, [new:Obj(X)[IitJi:B;/XI ielns), Ii:'v'(X<:A)X--.?B;/XI ieSub)

16.6.3 Accessing Classes from Objects


We can take advantage of Self types to increase the expressiveness of our classes.
We let an instance a of a class c generate new objects of its own class. To obtain an
object a with this capability, we include a method named fresh in a. When a.jresh is
invoked, it produces a fresh instance of c by executing c.new. Therefore the object a
should have the form:
a == obj(X=A)[fresh=c;(s:X)c.new, Ii=C;(s:X)b i ie1..n)
Each method body bi may contain invocations of s.jresh. Correspondingly, the type of
a should have the form:
A == Obj(X)[fresh:X, IitJi:B;lXI ie1..n)

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)

A typical class of this type has the form:


c :C £
[new=c;(z:C)obj(X=A)[fresh=C;(s:X)z .new, IFC;(s:X)z.Ii(X)(s) ie1..n),
IFA.(X<:A)A(s:X)bi ie1..n)
240 PART II. SECOND-ORDER CALCULI

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.

17.1 Syntax of Terms and Operational Semantics


We adopt untyped terms, as in our treatment of imperative calculi in Chapters 10 and
11. The terms are almost the same as the ones described in Section 10.1, but we adopt
a more complex method update construct that subsumes the let construct. This change
is due to the eager evaluation requirements of the imperative semantics, combined
with typing requirements for Self types.

17.1.1 Syntax of Terms


The syntax of the imp~-calculus is revised as follows:
Syntax of the imp~-calculus

a,b ::= terms


X variable
[Ii=~(xi)bi iEl..n] object (Ii distinct)
a.1 method invocation
a.I~(y,z=c)~(x)b method update
clone(a) cloning

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

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
242 PART II . SECOND-ORDER CALCULI

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.

17.1.2 Reduction Rules


The operational semantics is a variant of that of Chapter 10. It is given in terms of a
reduction relation between two stores (0, 0'), a stack (5), a term (b), and a result (v).
0·5 f- b -- v.o'
The only changes in the operational semantics are the new rule for method update
and the absence of let. The new rule is:
Operational semantics of method update
(Red Update)
0.5 f- a vo+ [li=Li i<1..nj.o' LjEdom(o') jEl..n
0' .(5, y ..... [li=Li i<1 .. n]) f- c vo+ v.o"

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.

17.2 Typing with Self


The types in this chapter are the same as the ones of system Sv of Chapter 16. In this
section, we use those of the subsystem 5, which include type variables, a Top type, and
object types of the form Obj(X)[lilJi:B;/X\ i<J..nj; the relevant rules are given in Section
16.2.3.
The type rules for terms borrow from both those of the first-order imperative cal-
culus and those of the functional calculus with primitive covariant Self types of Chap-
ters 11 and 16, respectively.
17. IMPERATIVE CALCULI WITH SELF TYPES 243

Terms
(Val Subsumption) (Val x)
E I- a : A E I- A <: B E', x:A, E" I- <)

El-a:B E', x:A, E" I- x: A

(Val Object) (where A;: Obj(X)[Ii'Ui:BiIXI i<I.."])


E, Xi:A I- bi : BiHAR ViEl..n
E I- [lj=C;(xj)b i iELn] : A

(Val Select) (where A';: Obj(X)[Ii'Ui:BiIXI i<I .."])


El-a:A E I- A <: A' UjE{°'+} jEl..n

(Val Update) (where A';: Obj(X)[I,'lJi:B;/XI i<I.."])


E I- a : A E I- A <: A' E, Y<:A, y: Y I- c : C
E, Y<:A, y:Y, z:C, x:YI- b: Bj(YR U;E{O,-} jEl..n
E I- a . lj~(y,z=c)<;(x)b : A

(Val Gone) (where A';: Obj(X)[I,'lJi:BiIXI i<I.."])


E I- a: A E I- A <: A'
E I- clone(a) : A

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

Syntax of the imp~v-calculus

a,b ::= terms


(as for impc;)
AOb type abstraction
aO type application

This choice of syntax is reflected in the operational semantics. We let a closure


(AOb,5) be a result and we add two rules to the operational semantics. According to
these rules, evaluation stops at type abstractions and is triggered again by type appli-
cations. This is a sensible semantics of polymorphism, particularly in the presence of
side-effects.
Operational semantics for polymorphism
v::= results
(as for impc;)
(AOb,5) type abstraction result

(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'

(Val Fun2<:) (Val AppI2<:)


E, X<:A I- b : B E I- b : V(X<:A)B(X} E I- A ' <: A
E I- AOb : V(X<:A)B E I- bO: BaA'»
17. IMPERATIVE CALCULI WITH SELF TYPES 245

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.

17.4.1 Encoding let


We begin by showing how the constructs of Chapters 10 and 11 can be represented in
the current calculus. The new method update construct can express both plain method
update and let:
[li:Bi iEl..nj £ Obj(X)[lio:Bi iE1..nj
a.lfFc;(x)b £ a.lfF(y,z=[])C;(x)b
let x=a in b/x} £ ([val=c;(y)y.valj.valfF(z,x=a)c;(w)b/x}).val
where y,z,w;'FV(b) and z;'FV(a), with x,y,z,w distinct
and X;'FV(B i ), for iE1..n
With these definitions, the first-order typing rules of Section 11.1 are derivable from the
typing rules in Sections 16.2.3 and 17.2. Moreover, the imperative operational seman-
tics of Section 10.5 can be emulated by the operational semantics of Section 17.1.2.
Since let is definable, we can obtain fields as in Section 10.2. However, a more
direct encoding of field update is now possible:
[li=bi iEl..n, lj=C;(xj)bj jEn+J ..n+mj £
let YJ=b J in ... let Yn=b n in [li=C;(YO)yi iEJ..n, lj=C;(xj)bjjEn+J ..n+mj
a.l:=b £ a.lfF(y,z=b)C;(x)z
where Yi;'FV(bk kEJ..n+m), Yi distinct for iEO ..n
and x,y,z;'FV(b) with x,y,z distinct

17.4.2 Alternative Forms of Method Update


In Part I we transformed a first-order functional calculus into a first-order imperative
calculus simply by adding the let and clone constructs, and modifying the operational
semantics. One might hope to proceed in the same way for calculi with Self types, to
define an imperative version of the calculus of Chapter 16. However, that is not what
we have done in this chapter: we have generalized the method update construct to
a.lfF(y,z=c)C;(x)b, and have not included a let construct. As we have just seen, the let con-
struct is definable; as we explain next, the new update construct is crucial in some
examples.
Suppose we have available only a more limited form of method update (analogous
to the one in Chapter 16), defined as:
a.lfF(y)C;(x)b £ a.lfF(y,z=[])C;(x)b
with the derived rule:
246 PART II. SECONO-OROER CALCULI

(Val Update') (where A ' == Obj(X)[hui:BiIXI i.l..n))


E f- a: A E f- A <: A '
E, Y<:A, y:Y, x:Yf- b : BjR¥) VjE{O,-} jEl..n
E f- a.lj~(y)r;(x)b : A

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 .

17.4.3 Protected Storage Cells


As a final example, we continue the discussion of storage cells (see Section 16.5.1) in an
imperative setting. We have:
17. IMPERATIVE CALCULI WITH SELF TYPES 247

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.

17.5 Subject Reduction


We adapt the subject reduction proof of Section 11.4 to the calculus with Self. The
resulting proof is lengthy and laborious, and many readers may want to skip it. We
include this proof in part to show how our methods apply to a fairly complex calculus,
and as an indicator of how subject reduction techniques may scale up to realistic pro-
gramming languages.
Two main changes are needed with respect to the proof of Section 11.4. The first is
a change in the definition of method type, since the Self variable occurs in the method
result types. The second is the introduction of type variables in environments, with the
corresponding need for type stacks in the stack typing judgment.

17.5.1 Typings with Stores


A method type has now the form:
M ;: Obj(X)[liWB;/X} iEl..n]=>j
The type of the self argument for a method of method type M is Obj(X)[lilJi:B;/X} iE1..n],
n.
and its result type is Bj(Obj(X)[I,vi:Bi{X} iE1.. n The index j on the right of => determines
the choice of Bj{X}.
A type stack is an association of type variables to types, of the form:
Ai closed types
We introduce an operation of substitution of a type stack T:
Substitution of type stacks
o{t-T} ~ 0

A{t-T} ~ A{Xit-Ai iE1..n}


(A' <: A){t-T) ~ (A'{t-T)) < : (A{t-T))
248 PART II. SECOND-ORDER CALCULI

('UA' <: 'UA'){~T) ~ 'U(A'{~T}) <: 'U(A{~T})


(a: A){~T) ~ a : (A{~T})

E{~T} is defined by:


12I{~T} ~ 121
(E, x :A){~T) ~ E{~T}, x:A{~T}
(E, X<:A){~T) ~ E{~T} if XEdom(T)
(E, X<:A){~T) ~ E{~T}, X<:A{~T} if Udom(T)

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

(Method Type) (I j distinct, \)jE(O,-:})


121, X<:Top f- B;/X+} jE1..n
1= Obj(X)[lj'Uj:B;/X} jE1..nl~j E Meth

(Store Type) (Ij distinct)


1= Mj E Meth ViEl..n
Lj ....Mj iE1..n 1= ¢

(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...

17.5.2 Proof of Subject Reduction


We state a few lemmas, without proof.
Lemma 17.5-1
(I) If E', X<:A, E"/X} f- ~/X} and E' f- A' <: A, then E', E"(A'} f- ~«A'l
(2) If E', x:A, E" f- ~, then E', E" f- ~, for ~ not of the form a: C.
(3) If E', X<:A, E" f- ~ and E f- A ' <: A, then E', X<:A', E" f- 53.
(4) If E', x:A, E" f- ~ and E f- A' <: A, then E', x:A', E" f- ~.

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.

If 1; 1= a : A and 1;' 1= 0 with 1;' ~ 1;, then l:' 1= a : A.


o
The subject reduction theorem is:
Theorem 17.5-4
If E f- a: A A o.S f- a -- v.o t A l: 1= a A dom(o) = dom(1;) A l: 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

o.(S', XI-+[I;=l; ;'l..nj, S") f- x __ [I;=l; ;.l..nj.o

By hypothesis, E f- x : A A l: 1= a A dom(o) = dom(l:) A l: 1= S. T: E where S == S', Xl-+[I;=l;


;'l..nj, S". Since E f- x: A, we must have E == E', x:A', En for some A' such that E' f-
A' and E f- A' <: A. Then, by Lemmas 17.5-2(3) and 17.5-1(2), 16 f- A'{f-T} <: A{f-T} .
Now l: 1= S. T : E must have been derived from l: 1= (S', Xl-+[I;=l; ;.l..n]). T' : E', x A: ',
for some prefix T' of T, and therefore l: 1= [I;=l; ;.l..nj : A'{f-T'} by (Stack x Typing).
Take At == A'{f-T'}. We have A'{f-T'} == A'{f-T}, because the free variables of A' are
included in dom(E'), and hence in dom(T') by Lemma 17.5-2(1), and hence At ==
A'{f-T}. Take l:t == l:.
We conclude 1;t 1= a A dom(o) = dom(l:t) A l:t 1= [I;=l; ;.l..nj: At A 16 f- At <: A{f-T}.
Case (Red Object) (I;, l; distinct)
o.S f- 0 l;~dom(o) 'v'iEl..n
o.S f- [I;=~(x;)b; ;.l..nj __ [I;=l; ;.l..nj.(o, l; ..... (~(x;)b;,S) ;'l..n)

By hypothesis, E f- [/;=~(x;)b; ;.l..nj : A A l: 1= a A dom(o) = dom(1;) A 1; 1= S. T: E. Since


E f- [/;=~(x;)b; ;.l..nj : A, we must have E f- [I;=~(x;)b; ;.l..nj : Obj(X)[I;u;:B;\X} ;.l..nj by
17. IMPERATIVE CALCULI WITH SELF TYPES 251

(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"

By hypothesis E I- a.lj : A /\ I: 1= 0 /\ dom(o) = dome!') /\ I: 1= S. T : E.


Since E I- a.lj : A, we must have E I- a.lj : Bject by (Val Select), with E I- Bjle» <: A,
and E I- a: C and E I- C <: D, where D has the form Obj(X)[ljvj:Bj(XI, ... ) and VjE (0, +).
By induction hypothesis, since E I- a: C ,,0.5 I- a -- [/i=Li iEI..II).o' "I: 1= 0 "dom(o)
= dom(I:) " I: 1= S·T: E, there exist a closed type A' and a store type I:' such that!"
~ I: /\ I:' 1= 0' /\ dom(o') = dom(I:') " I:' 1= [/i=li iE!..II) : A' "jill- A' <: C{(--T}.
Since o'(lj) = (~(xj)bj,S'), the judgment I:' 1= 0' must come via (Store Typing) from !,'
1= S'.jIl : Ej and Ej, Xj:!,' 1(lj) I- bj : I:' z{!,' I(Lj),Ij) for some Ej . Since!" 1= [/i=Li i<I .. II) : A' must
come from (Result Object), we have A' == Obj(X)[/,tI/:!,'2(X,li) i<I..II) == I:'I(lj). Since E
I- C <: D and I: 1= S. T : E, we obtain jill- C{f-T} <: D(f- T} by Lemma 17.5-2, and jill-
252 PART II. SECOND-ORDER CALCULI

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\

dom(o") = dom(Lt) A Lt 1= v: At 1\ fIl ~ At <: Bj«A'Hf-T}.


We conclude:
• Lt),:: L by transitivity from Lt),:: L' and L' ),:: L.
• Lt 1= 0" with dom(o") = dom('l:t).
• l:tl=v:At.
• fIl ~ At <: A{f-T}. Since E, X<:Top ~ Bj{X} and Bj{X} is covariant in X, we also have
fIl, X<:Top ~ Bj{XIIf-T} by Lemmas 17.5-2(3) and 17.5-1(2), with Bj{XIIf-T} covari-
ant in X. Since fIl ~ A' <: C{f-T}, we obtain fIl ~ Bj«A'B{f-T} <: Bj«C{f-Tl»!f-T}, by
Lemma 16.4-6, that is, fIl ~ Bj«A'B/f-T} <: BjiC&!f-T}. Since E ~ BjKi <: A, we obtain
fIl ~ Bj«q{f-T} <: A{f-T} by Lemmas 17.5-2(3) and 17.5-1(2). We conclude that fIl ~
At <: A{f-T} by transitivity from fIl ~ At <: Bj«A'B/f-T}, fIl ~ Bj«A'Hf-T} <: Bj«C&!f-T},
and fIl ~ Bj«q{f-T} <: A{f-T}.
Case (Red Update)
0.5 ~ a -- [li=Li iE1..").o' jEl..n LjEdom(o')
0' .(5, y..... [/i=Li iEl .. ")) ~ c -- v.o"
0.5 ~ a.lj~(y,z=c)C;(x)b -- [li=Li iE1..").(O" .Lj+-«C;(x)b, (5, y ..... [li=Li iE1.."), z .....v)})

By hypothesis E ~ a./j~(y,z=c)C;(x)b : A 1\ l: 1= 01\ dom(o) = dom(l:) 1\ l: 1= S. T: E.


Since E ~ a.lj~(y,z=c)C;(x)b : A, we must have E ~ a.lj~(y,z=c)C;(x)b : A' by (Val
Update), for some A' with E ~ A' <: A. Hence we have E ~ a: A' and E ~ A' <: D
where D has the form Obj(X)[Ij1!j:Bj {X}, ... ) and E, Y<:A', y:Y ~ c: C and E, Y<:A',
y:Y, z:C, x:Y~ b :Bj«YR and 1!jE{O,-}. Since Y;'dom(E), we have y;.dom(T) by Lemma
17.5-2(1).
By induction hypothesis, since E ~ a: A' 1\ 0.5 ~ a -- [/i=Li iE1..").o' 1\ l: 1= 0 1\ dom(o)
= dom(l:) 1\ l: 1= S.T: E, there exist a closed type At and a store type l:' such that l:'

),:: 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

By hypothesis E I- clone(a): A 1\ ~ 1= 01\ dom(o) = dom(~) 1\ ~ 1= S. T: E.


Since E I- clone(a): A, we must have E I- c/one(a): A'by (Val Clone), for some A'with
E I- a: A' and E I- A' <: D (where D is an object type), and such that E I- A' <: A.
By induction hypothesis, since E I- a: A' 1\ o.S I- a -- [li=li iEl ..n].o' 1\ ~ 1= 01\ dom(o)
= dom(~) 1\ ~ 1= S.T: E, there exist a closed type At and a store type~' such that~'
~ ~ 1\ r 1= 0' 1\ dom(o') = dom(r.') 1\ r.' 1= [li=li iE1..n) : At 1\ fill- At <: A'{f-T}.
= =
Let r.t (r.', li'-r.'(li) iE1..n) and ot (0', li' .... o'(li) iE1..n). We have ~t 1= 0 by (Store
Type) because l/;.dom(o') = dom(r.'), l/ are all distinct, and r.' 1= 0 (since r.' 1= 0').
We conclude:
• fill- At <: A'{f-T} <: A{f-T\, by Lemmas 17.5-2(3) and 17.5-1(2).
• r.t ~ r., because r.' ~ r. and r.t ~ r.'.
• dom(ot) = dom(r.t ), by construction and dom(o') = dom(r).
• We show that r.t 1= ot. Since r.' 1= 0' must come from (Store Typing), 0' is of the form
Ek....(~(Xk)b"'Sk) kE1..m, and for all kE l..m and for some Ek we have r.' 1= Sk.fII: Ek and E",
Xk:~' I(Ek) I- bk : r.' 2(r I(Ek),Ek). Then also E", Xk:~\ (Ek) I- bk : r.t2(r.t I(Ek),Ek), and r.t 1= Sk·fII
: Ek by Lemma 17.5-3. Let/: l..n~l..m be E-Iol, so that for all iE1 .. n, li= Efti). We have
Ej(i), Xj(i):r.' I(Ej(i» I- bj(i) : r 2(r.' I(Efti),Ej(i» for iE l..n, so Efti)' Xj(i):r I(li) I- bj(i) :
r.'2(r.'I(li),li). Moreover, since r.'(li) = r.t(l/), we have Ej(i), Xj(i):r.tl(li') I- bj(i) :
r.t 2(r.\(l/),l/). The result follows by (Store Typing) from r.t 1= Sk.fII : E", and r.t 1=
Sj(i)"fII : Efti)' and E", Xk:r.\(Ek) I- bk : r.t 2(r.\(Ek),Ek) and Ej(i), Xj(i):r.\(l/) I- bj(i) :
r.t 2(r.\(li'),l/)' for kEl..m and iEl..n.
• We show that r.t 1= [li=l/ iE1..n) : At. First, r.' 1= [/i=li iE1..n) : At must come from the
(Result Object) rule with At = ~'l(li) = Obj(X)[Ii\J/:r.' 2(X,li) iE1..n) for iEl..n, and r.' 1=
= = = =
o. But r.t(l/) r.'(lJ for iE l..n. So, r.\(li') r.' I(li) At Obj(X)[Ii\J/:r.' 2(X,li) iEI .. n) =
Obj(X)[liu/:r.t 2(X,l/) iE1..n), and by (Result Object) r.t 1= [li=li' iE1..n) : Obj(X)[Ii\Ji':
r.t2(X,l/) iE1..n).
Case (Red Fun2)

o.S I- AOb -- (AOb,S).o

By hypothesis, E I- AOb : A 1\ r.1= 0 1\ dom(o) = dom(r.) 1\ r.1= S. T: E.


From r.1= S.T : E and E I- AOb : A we have Elf-T} I- AOb : Alf-n by Lemma 17.5-
2(3). Since Elf-T} I- AOb : Alf-T} we must have E{f-T} I- AOb : V'(X<:C)B for some
type V'(X<:C)B such that Elf-T}, X<:C I- b: Band Elf-T} I- V'(X<:C)B <: Alf-T}.
We have r.1= S.fII: Elf-T} by Lemma 17.5-2(2), and hence r.1= (AOb,S): V'(X<:C)B by
(Result Fun2<:). Moreover, fill- V'(X<:C)B <: A{f-T} by Lemma 17.5-1(2), since
Elf- T} has no type variables.
17. IMPERATIVE CALCULI WITH SELF TYPES 255

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\

O. Ob,S): At 1\ 16 f- At <: A/f-T}.


1;t 1=

Case (Red Appl2)


a.S f- a -- (AOb,S')ocT cr' oS' f- b -- vocr"
a.S f- aO -- v.a"
By hypothesis, E f- aO : A 1\ 1; 1= a 1\ dom(a) = dom(1;) 1\ 1; 1= s. T : E.
Since E f- aO: A we must have E f- aO: B[q with E f- B{q <: A by (Val AppI2<:) for
some types C and V(X<:D)B{X} such that E f- a: V(X<:D)B/X} and E f- C <: D. We
obtain 16 f- C(f-T} <: D/f-T}, by Lemmas 17.5-2(3) and 17.5-1(2).
By induction hypothesis, since E f- a : V(X<:D)B 1\ a.S f- a -- (AOb,S').cr' 1\ 1; 1= a 1\
dom(a) = dom(1;) 1\ 1; 1= S.T: E, there exist a closed type A' and a store type 1;' such

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.

1S.1 The Interpretation Problem


Before describing particular interpretations of objects, we discuss the problem of find-
ing interpretations, and cover some preliminary material.

18.1.1 Encoding Objects and Subtyping


On a case-by-case basis, several techniques have been developed to write A-calculus
expressions that emulate object-oriented programs. To be reasonably satisfactory,
however, these emulation techniques should not be applied only to selected examples;
they should be systematized.
Object calculi provide a testbed: the adequacy of these emulation techniques can
be debated formally in terms of the existence of interpretations of object calculi within
A-calculi. We survey several such interpretations, pointing out their achievements and
their deficiencies. Many of these interpretations are well known, but some are new. In
particular, we introduce a new technique, developed in joint work with Viswanathan
[9], that successfully combines many of the ideas developed in the past. We do not deal
with techniques based on extensible records [43], because they require type systems for
A-calculi that are not discussed in this book.
The question stated above has a pragmatic analogue: why should we use object-
oriented languages, instead of emulating objects in procedural languages? If object-ori-
ented languages could be easily reduced to procedural languages, we could do without
them. The wide diffusion of object-oriented languages seems to suggest that this reduc-
tion is not easy or practical. Correspondingly, as we shall see, faithful interpretations
of object calculi in A-calculi are not easy to find. The available interpretations do not
lead to convenient programming techniques. Moreover, there is no canonical interpre-

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
258 PART II. SECOND-ORDER 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:

Syntax with Self types


I
Obj(X)[fk:Bk kE1..m I h:BiIX+} iE1..n] object type with m fields and n methods
obj(X=A)[f;:=bk kEl..m I li=<;(xi:X)bi/X,xi} iEl..n] object with m fields and n methods
oA,f field selection
oA,f:=b field update
oA·I method invocation
o.lj~(X<:A, y:X)<;(x:X)b method update (with old self)

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 Untyped Interpretations


We begin by reviewing several untyped interpretations of object calculi. These are used
as the basis of typed interpretations in the next section. The interpretations rely on
functions and records. We recall our notation for records: (/i=bi iE1..H) is a record; a·l is
record selection; and a·l:=b is record update.

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:

Untyped self-application interpretation


I
[li=<;(Xi)bi iEt ..n] ~ (li=A(Xi)bi iE l..H) (Ii distinct)
o.lj ~ o.lj(o) (jEl..n)
o . lj~<;(y)b ~ o.lr=A(y)b (jEl..n)
260 PART II. SECOND-ORDER CALCULI

As discussed in Section 6.7, this interpretation correctly represents the semantics of


both method invocation and method update, and it underlies most implementations of
object-oriented languages.
The self-application interpretation can be easily adapted to an imperative context;
cloning can be handled by an additional record component c1:
Untyped imperative self-application interpretation
I!/Fbk kEI .. m I li=~(Xi)bi iEI .. n] ~ (Jiv Ii, cl distinct)
(/k=bk h1..m,
li=A(Xi)bi iE1..n,
cl=A(x)(/k=x./k hl ..m, li=X .li iE1..n, cl=x.cl»
o,1i ~ o·1i (jEl..m)
o,Ii:=b ~ o·Ii:=b (jEl..m)
o.li ~ o.lj(o) (jE l..n)
o.li#'~(y)b ~ O.lr=A(y)b (jEl..n)
clone(o) ~ o.cl(o)

Here we distinguish fields from methods because, imperatively, they call for different
evaluation strategies.

18.2.2 Recursive Records


The recursive-record interpretation maps objects to recursively defined records [39,74,
106J. As discussed earlier in Section 6.7, the equational rule for update (even field
update) fails to hold under this interpretation, because update does not interact well
with recursion.
Untyped recursive-record interpretation without update
[li=~(Xi)bi{Xil iE1..n] ~ Jl(X)(li=b;{X& iE1..n) (Ii distinct)
o.li ~ o.li (jEl..n)

It is possible to modify the recursive-record interpretation so as to allow at least


internal field update. This is achieved by defining an auxiliary function init that is used
both for the field initialization and for the creation of modified objects during update.
Untyped recursive-record interpretation with internal field update
I!k=b k kE!..m I li=~(xi)bi{xil iE 1..n] ~
let rec init(Yk kEl .. m) = Jl(x)(/k=Yk kE1..m, li=b;{x& iELn)
in init(bk kELIn)
18. INTERPRETATIONS OF OB]Ecr CALCULI 261

o,/j ~ O./j (jE1..m)


o,/j:=b ~ init(o./k kEl ..j-\ b, O./k kEi+l..m) (jE 1..m) (internal)

0.1; ~ 0.1; (jE1..n)

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)

In this interpretation it is difficult to express the precise translation of the method


bodies, bi, essentially because the separation of fields from methods causes self to split
into two parts. Internal operations manipulate s directly, and are thus coded differently
from external operations. Moreover, since the self parameter s gives access only to
fields, internal method invocation is done through m:
Untyped state-application interpretation (continued)

Xi,/j ~ s./j in the context J.I.(m)(li=A(s) ... ) (jE1 ..m) (internal)

xi,/j:=b ~ s./j:=b in the context J.I.(m)(li=A(s) ... ) (jEt .. m) (internal)


xi.lj ~ m.lj(s) in the context J.I.(m)(li=A(s) ... } (j E 1.. n) (internal)
262 PART II. SECOND-ORDER CALCULI

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.

18.2.4 Cyclic Records


The main problems with functional interpretations are ultimately due to difficulties in
emulating state change. Side-effects trivially avoid these difficulties. Thus, in the pres-
ence of imperative features, additional encoding techniques become available (60).
The following cyclic-record interpretation is a version of the recursive-record
interpretation where recursion is replaced by loops in the store. In defining the inter-
pretation we use an informal imperative semantics for records. The construct (/k=bk
kEl .. m) allocates storage for the components /k, initializes them, and returns a reference
to the result. The construct a J=b is an assignment to the f component of the record a.
Untyped cyclic-record interpretation
I
l/k=bk k€J..m I li=<;(xi)bi{xi) i€l .. n) £ (flo Ii distinct)
let x = (jk=bk ke l..m, li=() iE 1..n) z~FV(bi iE1..n)v{x)
in X.li:=A(Z)bi«X} iE1..n; X
o,1i £ o·1i (jEl..m)
o,Ii:=b £ o·Ii:=b (jEl..m)
18. INTERPRETATIONS OF OBJECT CALCULI 263

o.lj g, o.lj(O) (jEl..n)


o.lj~~(y)b/y} g, (jEl..n)
let x = 0 in x.lj:=A.(z)b{xH ztFV(b)u/x}

In the interpretation of object formation, a little imperative program allocates a


skeletal record structure, and then fills it with methods by side-effects. As a result, dur-
ing the evaluation of the methods, the self variable points circularly to the record struc-
ture. The execution of method bodies is delayed by prefixing them with dummy
abstractions, which are stripped away on method invocation. On method invocation,
there is no need to pass the object to the methods explicitly because the self variable
already points to the record structure.
This interpretation handles both field update and method update. The treatment
of fields is straightforward. Method update is interpreted simply as an assignment to
a record field, again binding the self variable circularly. The update of a method can be
observed by all siblings of the method because their self variables point to the data
structure that is modified.
Nevertheless, the cyclic-record interpretation is not completely satisfactory. In an
imperative prototype-based language one expects to have cloning as one of the primi-
tives. Cloning, however, cannot easily be handled by cyclic records because recursion
has been tied too soon. The self variables of methods are bound to a fixed structure, and
copying the structure does not change those bindings. Hence, after cloning an object,
the self variables of the clone keep referring to the old object, instead of referring to the
clone.
We do not know how to modify the cyclic-record interpretation to allow cloning.
One way to try to solve the problem is to delay the binding of self. Then, however, we
are forced back into the self-application interpretation with cloning of Section 18.2.1.

18.2.5 Split Methods


The problem noted with the binding of init in the recursive-record interpretation can
be eliminated by splitting methods into two parts [9] . Each method Ii is represented by
two record components, 1/"1 and liupd; the former is used for method invocation, and the
latter for method update. For clarity, we rename in it to create. The occurrences of create
used for update are now encapsulated within the definition of create. This solution, it
turns out, supports even method update, so we do not distinguish fields from methods:
Untyped split-method interpretation
[li=~(xi)bi iEl..n] g, (Ii distinct)
let rec create(Yi iE 1..n) =
(l/el=Yi,
liupd=A.(y/)create(y/E1..i-l, y/, Yk kEi+l ..n) iEl..n)
in create(A.(xi)bi iE 1..n)
264 PART II. SECOND-ORDER CALCULI

o.l; ~ O.ltl(O) (jEl..n)


o.l;frc,(y)b ~ o.l/,pd(A.(y)b) (jE l..n)

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

code is passed on to create, which is invoked with a revised collection of functions to


produce a modified record. A method Ij is invoked by applying the corresponding
function It1 to 0, much as in the self-application interpretation.
The split-method interpretation matches the primitive semantics of objects. In this
respect it is just as good as the self-application interpretation. It is more complex, but
as we shall see it has better typing properties.

18.3 Typed Interpretations


In this section we investigate typed interpretations based on record types, function
types, recursive types, and existential types. Some of the untyped interpretations do
not yield the desired typing and subtyping properties. Some others do, but with con-
siderable complexity. Finally, we show that a typed version of the split-method inter-
pretation is both faithful and uniform. It has an imperative variant that shares these
characteristics.
The interpretations rely on record types. Record types have the form (/i:Bi iE!..n);
they are covariant in their components for functional interpretations, and invariant for
imperative interpretations (because of side-effects on record fields).

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}

[li=C,(xi:A)bi iE!..n) ~ fo1d(A,{li=A.(xi:A)bi iE!..n»


o.l; ~ r.mfold(o )./j(o) (jE l..n)
o.ljfrc,(y:A)b ~ fold(A,unfold(o ).I;:=A.(y:A)b) (jE l..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

18.3.2 Recursive Records


We can try to obtain an interpretation that validates the subtyping rules for object types
by interpreting object types as (non-recursive) record types [39) :

Recursive-record interpretation, without update


A == [/i:Bi iE I..n) g, (Ii distinct)
(li:Bi iEl ..n)
[li=~(Xi:A)bi{Xi) iE1..n) g, ll(x:A)(li=b;ixH iE!..")

o.lj g, o.lj (jEl..n)

As we said in Section 18.2.2, a uniform update primitive is precluded by this inter-


pretation. However, it is still possible to achieve the effect of internal updates by the
init technique. We show this by translating an object calculus with Self types:
Recursive-record interpretation with internal field update
I A == Obj(X)[/k:Bk kE1..m Ili:B;\X+) iE 1..n) g, (/k, Ii distinct)
Il(X)(/k:Bk kE1..m, li:B;\Xl iEl .. n)
obj(X=A)(fk=bk kE!..m I li=~(Xi:X)b;{X,Xi) iEl .. n) g,
let rec init(Yk:Bk kE!..m) :A = ll(x:A)jold(A,(fFYk kEl..m, li=b;«A,xH iEl..n»
in init(bk kEl..m)
OoJi g, unfold(o ).Ji (jEl..m)
ooJi:=b g, init(unfold(o)fk kE1..j-l, b, unfold(o)fk kEi+1..m) (jEl..m) (internal)
o.lj g, unfold(o).l j (jE1..n)

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

xiof;:=b £ s·fJ :=b (jE1..m) (internal)


xi.li £ m.lj(s) (jE l..n) (internal)
OA'!j £ open 0 as X, p:C{XI in p.mt.lj(p.st) : Bj (jEl .. n) (external)
for X~FV(Bj)
oA.li £ (jE l..n) (external)
open 0 as X, p:c{XI for Bj == X
in (pack X'=X with (st=p .mt.lj(p.st), mt=p .mt) : CiX'D)
:A

The translation of internal operations on self is problematic, as we observed earlier.


External method invocations also have to be handled carefully; in the table we deal
with method invocation for the cases where X~FV(Bj) and where Bj == X. Other possi-
bilities arise; the general case requires code generation driven by the structure of the
object types [72J.
The difficulties with this typed interpretation can be resolved, and then both invo-
cation and internal field update work well, yielding encodings of class-based lan-
guages. However, there are no visible advantages of this existential interpretation with
respect to the recursive-record interpretation given at the end of Section 18.3.2; even
the hiding of fields can be obtained there by subtyping. The similarity is not completely
accidental; these two interpretations can be formulated as special cases of a single
scheme [721 .

18.3.4 Cyclic Records


For the typed version of the cyclic-record interpretation, we write nil(C) for the "unini-
tialized" value of type C:
Cyclic-record interpretation
I
A == lfk:Bk kE1..m 11i:Bi iE1..n] £ (Ii distinct)
<fo:Bk kEI ..m, li:()~Bi iE l..n)
lfk=bk kE1..m I li=<;(xi:A)b;{xil iE1..n] £
let x:A = (/k=b k kE1..m, li=nil«)~Bi) iEl..n)
in x .li:=A(z:(»bi[xD iE1..n; X z~FV(bi iEl .. n)v(xl

oofJ £ a·fJ (jE1..m)


oofJ:=b £ o·fJ:=b (jE1..m)
o.li £ o.lj«» (jE1..n)
o . lj~<;(y:A)b{yl £ (jE l..n)
let x:A = 0 in x .l;:=A(z :O)b(xD z~FV(b)v(xl
268 PART II. SECOND-ORDER CALCULI

This interpretation has the expected behavior in an imperative context. Moreover,


the self parameters are not reflected in the types of objects; thus the desired subtyping
properties are obtained.
Cyclic records can be exploited further to obtain typed encodings of class-based
imperative languages [60). However, as discussed earlier, it does not seem possible to
extend this interpretation to cloning, short of turning it into a self-application interpre-
tation. But, if we do that, we run into the typing difficulties of self-application.

18.3.5 Split Methods


Building on the ideas described so far, we introduce interpretations that we find almost
completely satisfactory. These are typed versions of the split-method interpretation.
A first idea for typing the code of the split-method interpretation could be to set:
[li:Bi iE1..n] £ Il(X)(I/el:X~Bi iEt .. n, liupd:(X~Bi)~X iE1..")
but the body of this recursive type contains contravariant occurrences of X and there-
fore this definition does not yield the desired subtypings.
A second idea, due to Viswanathan (123), is to consider a similar type without the
contravariance occurrences of X in the types of the Irl components:
[li:Bi iE1.."] £ Il(X)(W I:Bi iE1..n, liupd:(X~Bi)~X iE1.."}
We can adapt the untyped split-method interpretation to match this encoding of object
types:
Split-method interpretation (initial version)
A == [li:Bi iE1..n] £ (Ii distinct)
Il(X)(//el:Bi iE1..n, liupd:(X~Bi)~X iEJ..n)
[li=C;(Xi:A)bi iE1..n] £
let ree ereate(Yi:A~Bi iEl..n):A =
jold(A,
(/rl=Yi(ereate(Yi iE I..n» iE I..n,
l;"pd=A(y;':A~Bi)create(YiiE1..i-l, y/, Yk kEi+l..n) iE1..n»
in ereate(A(xi:A)bi iEt .. n)
OA.lj £ unjold(o).l/,l (jEl..n)
o./j~C;(y:A)b £ unjold(o).ltpd(A(y:A)b) (jE l..n)

Note that method invocation is no longer modeled by self-application. Instead, a kind


of self-application happens whenever an object is constructed: 1/,I=Yi(ereate(Yi iE1..n»
applies the function Yi to a freshly created copy of self.
This interpretation validates the subtyping rules for object types, since all occur-
rences of X, bound by Il, are covariant; and it also validates the typing rules for objects.
Moreover, it behaves as expected with functional semantics. (It is written for call-by-
18. INTERPRETATIONS OF OBJECf CALCULI 269

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

As an example, we translate the following type and term:


Cell ~
Obj(X)[get:Nat, set:Nat--7Xj
cell: Cell ~
obj(X=Cell)[get = C;(s:X) 0, set = C;(s:X) A(n:Nat) s.get ~ C;(s':X) n)
The translations are:
18. INTERPRETATIONS OF OBJECT CALCULI 271

Cell == ~(Y) 3(X<:Y) C{X}


where C{X} == (r:X, gerel :X--7Nat, setsel:X--7(Nat-tX),
getupd:(X --7Nat)--7X, setupd:(X -t(Nat--7X»--7X}
cell ==
let rec create(get:Cell--7Nat, set:Cell--7(Nat--7Cell»:Cell =
fold(Cell,
pack X = Cell
with
(r = create(get, set),
get sel = get,
set"l = set,
get upd = 'A(get':Cell--7Nat) create(get', set),
setupd = 'A(set':Cell--7(Nat--7Cell) create(get, set')}
: C{Xj)
in create('A(s:Cell) 0,
'A(s:Cell) 'A(n:Nat)
open unfold(s) as X<:Cell, y:( . .. , getUpd :(X--7Nat)--7X}
in y.getupd('A(s':X) n)
: Cell)
As we can see from this example, the split-method interpretation may be adequate for
representing objects as records, but is overwhelming as a programming technique.
The split-method interpretation handles an interesting source calculus that resem-
bles the one of Chapter 16 (especially when variance annotations are added). As given,
the interpretation does not support structural rules, which are desirable in conjunction
with Self types. However, we believe that those rules can be accounted for by introduc-
ing structural rules in the target calculus.

18.3.6 From Split Methods back to Self-Application, Imperatively


When we consider an imperative version of the split-method interpretation, we dis-
cover that it is no longer necessary to split methods, because updates can be handled
imperatively [9). The remaining structure of the interpretation is still interesting; judg-
ing by the treatment of method invocation, it amounts to a self-application technique.
The main technical novelties are the repacking performed in the update operations and
the treatment of cloning.

Imperative self-application interpretation


A == lfk:Bk kEl ..m Ilj:Bj jEl ..lIj ~ (fie, l; distinct)
~(Y)3(X<:Y)C{X)
with C{XJ == (r:X,/k:Bk kEl ..m, lj:X-tBj jEl..n, ci:()-tX}
272 PART II. SECOND-ORDER CALCULI

Ifrbk kE1..m I Ii=~(xi:A)bi iE 1..n) ~


let ree ereate(Yk:Bk kE1..m, Yi:A~Bi iEJ..n):A =
let z:«A} = (r=nil(A), ik=Yk kEJ..m, li=Yi iE1..n, cl=nil«)~A»)
in z.r:=fold(A,paek X<:A=A with z: «X});
z.cl:='A.(x:()create(z.jk kEl..m, z.lj jEl..n);
z·r
in create(bk kE J..m, 'A.(xj:A)bi iE1..n)
OA,!i ~ open unfold(o) as X< :A, p:C{X} in P'!i: Bj (je1..m)

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.

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
274 PART II. SECOND-ORDER CALCULI

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

a,b,c ::= terms


x variable
object(x:X=A) li=bi iE1 .. n end direct object construction
a.l field selection / method invocation
a.I:= b update with a term
a.l:= method(x:X<:A) bend update with a method
newc object construction from a class
root root class
subclass of c:C with(x:X<:A) subclass
li=b; ;En+1..n+m additional attributes
override li=bi iEOvr<;;1..n end overridden attributes
c"I(A,a) class selection
fun(X<:A) bend type abstraction
b(A) type application

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

eq = fun(other: Coord) selfx = other.x end,


mv = fun(dx: lnt) selfx := selfx+dx end
end
cPointClass: Class(CPoint) £
subclass of pointClass: Class(Point)
with (self: X<:CPoint)
c = black
override
mv = fun(dx: lnt) super.mv(dx).c := red end
end
The subclass cPointClass overrides the method mv, but this override is optional.
The method mv could just as well be inherited, because in both classes it is required to
return a result of the Self type. In cPointClass, the method mv invokes super and then
modifies the result, updating its color to red. There super.mv means pointClassAmv(X,
self), which has type X; the update to red preserves this type.
The method eq from pointClass is simply inherited into cPointClass, and therefore it
ignores color information. To override eq with a version that tests the color of self and
other, we would need typecase, as in Chapter 12.
We can use cPointClass to create color points of type CPoint:
cPoint : CPoint £ new cPointClass
Thanks to Self types, calls to mv preserve the color information without need of type-
case either in the definition of mv or in its use:
movedColor: Color ~ cPoint.mv(1).c

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)

For subtyping, we have:

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

(Sub Object) (where A == Object(X)[I,1)j:Bj{X} j<I ..n+mj, A' == Object(X')[I,1)j':Bj'{X'} <


j l..n])
E~ A E ~ A' E, X<:A ~ 1)i Bi(X} <: vi' B/lX} ViEl..n
E~A<:A'

(Sub Invariant) (Sub Covariant) (Sub Contravariant)


E~B E ~ B <: B' VE(o,+} E ~ B' <: B VE(O,-}
E ~ ° B<: °B E~vB<: +B' E~vB <: -B'

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

E~a : B E', x:A, E" ~ x : A

(Val Object) (where A == Object(X)[I,1)j:Bj{X} jEt ..n])


E, x:A ~ MAl: BilA. ViE l..n
E f- object(x:X=A) l;=bi(X} iE!..n end: A

(Val Select) (where A == Object{X)[I;Uj:BiIX} .iLn])


E~a :A' E ~ A' <: A VjE(o,+} jEl..n
E ~ a.lj : BjIA')

(Val Update) (where A == Objecl(X)[I,1)j:Bj{X} JEI .. n])


E ~ a: A' E ~ A' <: A E ~ b : Bj X~FV(Bj) VjE(O,-} jEl..n
E ~ a.lj:= b : A'

(Val Method Update) (where A == Object(X)[I,1)j:BjIX} iOLn])


E~a : A' E~A'<:A E,X<:A', x:X~b:Bj (X} VjE(O,- } jEl..n
E ~ a.lj := method(x:X<:A') bend: A '
19. A SECOND-ORDER LANGUAGE 281

(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)

(Val Class Select) (where A:; Object(X)[I,vi:Bi(XI iE1..n])


E I- a: N E I- N <: A E I- c: Class(A) jEl..n
E I- cAliA',a) : BjHNB

(Val Fun<:) (Val App\<:)


E, X<:A I- b : B E I- b: All(X<:A)B(Xl E I- A' <: A
E I- fun(X<:A) bend: All(X<:A)B E I- b(A') : BHA'»

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

in A can be a subtype of the corresponding component type in A'. Thus, in general,


there is no type specialization for fields, but there may be type specialization for meth-
ods; for methods, there is Self type specialization.
As in 0-1, not all methods of a class for A' can be inherited in a class for A . Method
Ij is inheritable if E, X<:A \- Bj' <: Bj where Bj' and Bj are the result types in A' and A,
respectively. This condition holds when B;' and Bj are identical, for example when they
are both equal to the Self type variable X; therefore methods that return self are inher-
itable. In this respect 0-2 is more sophisticated and liberal than 0-1.

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»

This translation of types induces a translation of environments:


Translation of 0-2 environments
«!ii» ! !ii
«E, X<:A» ! «E», X<:«A»
«E, x:A» ! «E», x:«A»

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

Translation of 0-2 terms

«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.

20.1 Syntax of ObW<:/l


The main new feature of Db<U<:1l consists of type operators such as, for example:
A(X)V'(Y<:X)Y

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

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
288 PART III. HIGHER-ORDER CALCULI

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).

20.2 Operational Semantics


As usual, the operational semantics is based on reduction judgments of the form f- b .....
v, where b is a term and v is a result. Results are a subset of the terms, and they cannot
be further reduced.
Results are described by the following grammar:
Results
v results
[lj=C;(Xj :Aj)bj jE1..n) object result
A(X<:A::K)b constructor abstraction result
jold(A,v) recursion result

The reduction relation is defined by the following rules:


Operational semantics
(Red Object) (where v '" [lj=~{xj:Aj)bj i<1.."))

(Red Select) (where v' '" [Ii=<;{xi :Ai)b;{xil JE I.."))

f- a "'+ v' f- bjlv'} ..... v jE l..n


f- a./j ..... v

(Red Update)
f- a ..... [lj=C;(xj:Aj)bj jEl..n) jEl .. n

(Red Fun2::) (where v'" A{X<:A::K)b)

(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

(Con Abs) (Con Appi)


E, X::K I- B : : L E I- B ::K=}L E f- A :: K
E f- B(A):: L

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 EqX) (Con Eq Top)


E I- X:: K Ef-o
Ef-X -X:: K E f- Top .... Top :: Ty
292 PART III. HIGHER-ORDER CALCULI

(Con Eq Object) (Ij distinct, \JjE{O,-:}) (Con Eq All)


E r Bi - B;' 'v'iEl..n ErA - A' :: K E, X<:A: :K r B - B'
E r 'v'(X<:A ::K)B - 'v'(X<:A'::K)B'

(Con Eq Rec)
E, XrB- B'
E r J.1(X)B ++ J.1(X)B'

(Con EqAbs) (Con Eq Appl)


E, X::K r B - B' :: L E r B - B' :: K=*L ErA - A' :: K
E r A(X::K)B - A(X::K)B' :: K=*L E r B(A) - B'(A ' ) :: L

(Con Eva) Beta)


E, X::K r B(X) :: L ErA :: K
E r (A.(X::K)B(X))(A) - BfA» :: L

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

(Con Sub X) (Con Sub Top)


E', X<:A::K, En r 0 ErA :: Ty
E', X< :A: :K, En r X <: A:: K ErA <: Top :: Ty

(Con Sub Object) (Ii distinct)


E r Vj Bi < : V j ' B;' 'v'iE l..n E r Bi 'v'iEn + l..n + m
E r [liV j: Bj jEl..n+m) <: [1;tJ;':B;' iEl..n)

(Con Sub All)


ErA ' <: A :: K E, X< :A '::K r B <: B'
E r 'v'(X<:A::K)B <: 'v'(X<:A':: K)B '

(Con Sub Rec)


E r J.1(X)A E r J.1(Y)B E, Y, X< :Y r A <: B
E r J.1(X)A <: J.1(Y)B
20. A HIGHER-ORDER CALCULUS 293

(Con Sub Abs) (Con Sub AppI)


E, X::K f- B <: B' :: L E f- B <: B' :: K~L E f- A :: K
E f- A(X::K)B <: A(X::K)B' :: K~L E f- B(A) <: B'(A) :: L

(Con Sub Invariant) (Con Sub Covariant) (Con Sub Contravariant)


Ef-B E f- B <: B' VE (O, +) E f- B' <: B VE (O, -)
E f- °B <: °B E f- V B <: + B' Ef-vB<:-B'

Term typing
(Val Subsumption) (Val x)
E f- a : A E I- A <: B E', x:A, E" f- ¢

Ef-a:B E', x:A, E" f- x: A

(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

(Val Update) (where A == IhwBi it!.."))


E f- C <: A E f- a : C E, x:C f- b : Bj jEl..n
E f- a.ljfrc;(x:C)b : C

(Val Fun2::) (Val AppI2::)


E, X<:A::K f- b : B E f- b: 'V(X<:A::K)B{X) E f- A' <: A :: K
E f- A(X<:A::K)b : 'V(X<:A::K)B E f- b(N) : BiA'»

(Val Fold) (Val Unfold) (where A == Il(X)B{X})


E f- b : BM) E f- A - Il(X)B{X) Ef-a:A
E f- fo1d(A,b) : A E f- unfold(a): BM}

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

20.4 Binary Methods


The treatment of Self types in Part II is restricted to covariant occurrences; there, that
restriction is necessary in order to keep subtyping and subsumption working
smoothly. As we discussed in Chapter 3, one may want to give up certain subtyping
properties in exchange for a treatment of general Self types (Self types without the
covariance restriction) [36). A proper treatment of general Self types naturally involves
higher-order constructions [4), and hence was delayed until this chapter.

20.4.1 Binary-Tree Objects


The covariant Self types of Part II were motivated by a number of examples, of which
the purest was perhaps the typing of the numerals. A slight modification of that exam-
ple shows the need for binary methods and general Self types.

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

consLft = c,(self:UBin) 'A(lft:Bin)


fold(Bin, ((selfisLeaf:= false).lft:= lft).rht:= fold(Bin, self»,
consRht = c,(se/f:UBin) 'A(rht:Bin)
fold(Bin, ((selfisLeaf:= false).lft := fold(Bin, self».rht := rht)]

Here, as usual, we write a.l:=b for a.l~c,(x:A)b when x~FV(b).


For example, a tree with two leaves and a joining root can be written:
unJold(leaJ).consLft(leaJ) : Bin

20.4.2 Binary-Tree Classes


We wish to define a class type BinClass, and a class binClass of type BinClass that gen-
erates trees of type Bin. Our intent is to write BinClass and binClass so that the methods
of binClass can be inherited. An inheriting class could, for example, generate trees with
nodes containing natural numbers. Such trees would have type:
NatBin ~ J.1(X)[n:Nat, isLeaf:Bool, lft:X, rht:X, consLft:X--7X, consRht:X-"7X]
Note that NatBin <: Bin cannot hold, but we still aim to reuse, for example, the consLft
binary method.
Working towards this goal, we first transform the type Bin into a type operator
BinOp. We introduce the following notation in order to facilitate working with type
operators:
Notation
• Op is the kind of simple type operators; it stands for Ty~Ty.

• A -<: B means that A is a suboperator of B; it stands for A <: B :: Op.


• A * is the fixpoint of the operator A. It stands for the type J.1(X)A(X) where A:: Op.
We define the operator BinOp simply by converting the recursion binder of Bin
into an abstraction binder:
Object-oriented binary-tree operator
BinOp:: Op ~
A(X)[isLeaf:Bool, lft:X, rht:X, consLft:X-"7X, consRht:X-"7X]
Bin :: Ty ~ BinOp*
UBin :: Ty ~ BinOp(Bin)

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*

rf 9$, X<.:BinOp, YI- X:: Op


9$, X<':BinOp, Yr Y <: Top
9$, X <.:BinOp, Y r X(Y)
by (Con X)
by (Con Sub X)
(Con Appl)
9$, X<.:BinOp r ~(Y)X(Y) (Con Rec)

Ex. 2: 9$, X<.:BinOp r X(X*) <: BinOp(X*)

f9$, X<.:BinOp r X <.: BinOp


9$, X <.:BinOp r X* <: Top
by (Con Sub X)
similar to Ex. 1
9$, X<.:BinOp r X(X*) <: BinOp(X*) (Con Sub Appl)

Ex. 3: 9$, X<.:BinOp, x:X* r unfold(x): X(X*)


(9$, X <.:BinOp, x:X* r x : X* by (Env x)
9$, X<.:BinOp, x:X* I- unfold(x): X(X*) (Val Unfold)

Ex. 4: 9$, X<.:BinOp, x:X*1- unfold(x).lft:=x: X(X*)

r 9$, X <.:BinOp, x:X* r X(X*) <: BinOp(X*)


9$, X<.:BinOp, x:X* r unfold(x) : X(X*)
9$, X<.:BinOp, x:X*, y:X(X*) r x: X*
by Ex. 2
by Ex. 3
by (Env x)
9$, X<. :BinOp, x:X* r unfold(x).lft~,-(y:X(X*»x : X(X*) (Val Update)
20. A HIGHER-ORDER CALCULUS 297

Ex. 5:J/S, X<.:BinOp, x:X* f- unfold(x).consLft(x) : 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.

20.5 Basic Properties of Ob!l)<:11


In the rest of this chapter we study properties of Ob(»<:1V proving a subject reduction
result; we imitate Compagnoni's work on a different higher-order calculus [511. Discus-
sion about applications of Ob(»<:11 resume in Chapter 21.
Direct proofs by induction on derivations rarely work in Ob(»<:11 because the!) rule
(Con Eval Beta) introduces expressions of arbitrary shape. In this section we define a
normal system: a subsystem of Ob(»<:11 that lacks the!) rule, but that is sound and com-
plete with respect to Ob(»<:11 over normal forms at the constructor level. The purpose of
the normal system is to help us analyze subtyping and constructor equivalence; there-
fore, the normal system is not concerned with typing judgments. We will be able to
carry out proofs by induction on the derivations of the normal system, and then trans-
fer lemmas to Ob(»<:w
We begin by stating some basic properties of Ob(»<:w
Lemma 20.5-1
(1) Bound weakening
If E, X< :A::K, E' f- g and E f- A':: K and E f- A' <: A:: K,
then E, X<:A' :: K, E' f- g.
If E, x:A, E' f- g and E f- A' and E f- A' <: A,
then E, x:A', E' f- g.
(2) Type substitution
If E, X<:A::K, E'{X} f- g{X} and E f- A' :: K and E f- A' <: A :: K,
then E, E'IA'a f- g«AT
(3) Value substitution
If E, x:A, E' f- g{x} and E f- a: A, then E, E' f- g(4
(4) Maximum of a kind
If E f- K kind, then E f- fKl:: K.
If E f- A :: K, then E f- A <JK1:: K.
o
The definition of the normal system relies on the notions of normal form Nf of a
constructor A, and of the least upper bound lubE(A) of a constructor A in an environ-
ment E. The least upper bound is defined only for terms of the form X(Aj) .. . (An), for n
~ O. If the immediate bound of X in E is B, then lubE(X(Aj) ...(An)); B(Aj) ... (An).
20. A HIGHER-ORDER CALCULUS 299

Least upper bounds and normal forms


lubE,X<:A,E'(X) g, A
lubE(B(A)) g, lubE(B)(A)
lubE(A) undefined otherwise
Xn! g, X
Topn! g, Top
[lNi:Bi i€!..n]n! g, [livi:Bin!i€!.."]
(V(X<:A::K)B)n! g, V(X<:A"!::K)B n!
(f.l(X)A)"! g, f.l(X)A n!
(A(X: :K)B)n! g, A(X::K)B n!
=
(B(A»"! g, if Bn! A(X::K)C{XI for some X,K,c, then (CHAW! else Bn!(An!)
</In! g, </I
(E, X<:A::K)n! g, En~ X<:A"!::K
(E, x:A)n! g, En~ x:A"!

We omit a proof that these definitions are proper.


The normal system replaces the judgments E f- A <: B :: Kand E f- vA <: v'B of
Obox:J.l with the judgments E f-n A <: B :: K and E f-n vA <: v'B. The judgments E f- 0, E
f- K kind, and E f- A :: K are unchanged, and their definition is not repeated. The judg-
ment E f- A - B :: K is dropped because we intend to avoid the problems caused by ~­
equivalence.
Judgments for the normal system
Ef-o E is an environment
E f- Kkind K is a kind
Ef-A :: K constructor A has kind K
Ef- n A <: B:: K A is a subconstructor of B, both of kind K
E f-n vA <: tJ' B A is a subtype of B according to variances tJ and tJ'

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

Nonnal constructor inclusion


(NCon Sub Refl) (NCon Sub Trans)
E f- A:: K E f-n A <: B :: K E f-n B < : C :: K
E f-n A <: A :: K E f-n A <: C ::K

(NConSubX) (NCon Sub Top)


E', X<:A::K, E" f-n An! <: B :: K E f- A:: Ty
E', X<:A::K, E" f-n X <: B :: K E f-n A <: Top :: Ty

(NCon Sub Abs)


E, X ::K f-n B <: B' :: L
E f-n A(X::K)B <: A(X::K)B' :: K~L

(NCon Sub Appl) (when lubdB(A» is defined)


E f- B :: K~L E f- A :: K E f-n (lubE(B(A)))n! <: C :: L
E f-n B(A) <: C : : L

(NCon Sub Object) (Ii distinct)


E f-n Vi Bi <: V;' Bj ' '<tiE l..n E f- Bi '<tiEn+ l..n+m
E f-n [livi:Bi iE1..n+mj <: [1;V;':Bi' iE1..nj

(NCon Sub All)


E f-n A' <: A:: K E, X< :A'::K f-n B <: B'
E f-n '<t(X<:A ::K)B <: '<t(X<:A': :K)B'

(NCon Sub Rec)


E f- Il(X)A E f- Il(Y)B E, Y, X<: Y f-n A <: B
E f-n Il(X)A <: Il(Y)B

(NCon Sub Invariant) (NCon Sub Covariant) (NCon Sub Contravariant)


Ef-B E f-n B <: B' VE {o,+} E f-n B' <: B VE to, -}
E f-n °B <: °B E f-n V B <: + B' E f-n vB <: - B'

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

Lemma 20.5-2 (Equivalence to normal form)


If E I- A :: K, then E I- A H N!:: K.
o
Lemma 20.5-3 (Substitution for unbounded type variables)
If E, X::K, E'{X} I-n g{X} and E I- A:: K, then E, (E'gAW!l- n (g(AW~
= =
where g B <: B' :: K or g \J B <: v' B'.
o
Lemma 20.5-4 (Normal inclusion of operator application)
If E I-n A <: B :: L~K and E I- C :: L, then E I-n (A c)n! <: (B c)n! :: K.
o
Lemma 20.5-5 (Soundness and completeness of normal system)
If E I-n A <: B:: K, then E I- A <: B :: K.
If E I-n v B < : v' B', then E I-v B <: v' B'.
If E I- A <: B:: K, then En!f- n An! <: Bn!:: K.
If E I- vB <: v' B', then En! l- n v Bn! <: v ' B'n!.
o
The normal system allows us to prove lemmas about subtyping in ObOK: I1. The lem-
mas are first proved for the normal judgments, and then transferred to ObOK :11 by
soundness and completeness.
Lemma 20.5-6 (Structural sub typing)
(1) If E I- Top <: C, then E I- C H Top.
(2) Let E I- V(X<:D::L)C <: V(X<:D'::L')C.
=
Then L L', E I- D' <: D ::L, and E, X<:D'::L I- C <: C.
(3) Let E I- [l{Ui:Bi ieI) <: [l{U/:B/ ie,).
(I)J ~ I.
(2) If v/E{°,+) for some JEJ, then ViE {O,+} and E I- Bi <: B(
(3) If V/E {O,-} for some JEJ, then ViE {O,-} and E I- B/ <: Bi"
(4) Let E I- ~(X)B{X) < : ~(X')B'{X').
Then either E, X I- B{X} H B'iX}' or E, X', X<:X' I- B{X} <: B'{X'}.
o

20.6 Subject Reduction


Our main result for ObOK:" is a subject reduction theorem. The proof is quite standard:
all the higher-order difficulties have been hidden in the lemmas of the previous section.
302 PART III. HIGHER-ORDER CALCULI

Theorem 20.6-1 (Subject reduction)


If ¢ f- a : A and f- a v-+ v, then ¢ f- v : A.
Proof
By induction on the derivation of f- a v-+ v.
Cases (Red Object) and (Red Fun2::)

Immediate, since a == v.
Case (Red Select) (where v' == [li=~(Xi:Ai)Mxi) iEl .. n])

f- c -- v' f- bj(v" -- v jE1..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

Case (Red Unfold)


f- c ~ jold(C,v)
f- unjold(c) ~ v

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.

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
306 PART III. HIGHER-ORDER CALCULI

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

Syntax of 0-3 terms


a,b,c ::= terms
x variable
object(x:A) l;=b; ;E1..n end direct object construction
a.l field selection / method invocation
a.l := method(x:A) bend update with a method
newc object construction from a class
root root class
subclass of c:C with(x:X<#A) subclass
l;=b; ;En+l..n +m additional attributes
override l;=bi iEOvr!;1..n end overridden attributes
cAI(A,a) class selection
fun(X<#A) bend match-bound type abstraction
b(A) match-bound type application

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.1 Binary Trees


First we rewrite, more compactly, the binary-tree example of Section 20.4.1. The code
for binary trees in 0-3 is reasonably simple: there is no explicit mention of higher-order
operators, subtyping, fold, or unfold; the code looks like program source.
Bin ~
Object(X)[isLeaf. Bool, 1ft: X, rht: X, consLft: X~X, consRht: X~Xl

bin Class : Class(Bin) ~


class with(self. X<# Bin)
isLeaf = true,
1ft = self 1ft,
rht = self.rht,
consLft = fun(lft: X) «selfisLeaf:= false )·lft := lft). rht := self end,
consRht = fun(rht: X) «self.isLeaf:= false).ift := self). rht:= rht end
end
leaf: Bin ~
new bin Class
The definition of the object type Bin is the same one we could have given in 0-1,
but it would be illegal in 0-2 because of the contravariant occurrences of X. The
method bodies rely on some new facts about typing; in particular, if self has type X and
X<#Bin, then self.lft and self.isLeaf.=false have type X.
Let us consider now a type NatBin of binary trees with natural number compo-
nents. This type is not a subtype of Bin. However, Nat Bin matches Bin because it has
more components and is otherwise identical to Bin.
NatBin ~
Object(X)[n: Nat, isLeaf Bool, 1ft: X, rht: X, consLft: X~X, consRht: X~XJ
Thus we have NatBin <# Bin. If b has type Bin and nb has type NatBin, then b.consLft(b)
and nb.consLft(nb) are allowed, but b.consLft(nb) and nb.consLft(b) are not.
The methods consLft and consRht can be used as binary operations on any pair of
objects whose common type matches Bin. Therefore 0-3 allows inheritance of consLft
and consRht. A class for NatBin may inherit consLft and consRht from binClass.
Because Nat Bin is not a subtype of Bin, generic operations must be explicitly
parameterized over all types that match Bin. For example, we may write:
selfCons: All(X<#Bin)X~X ~
fun(X<# Bin) fun(x: X) x.consLft(x) end end
selfCons(NatBin)(nb): Nat Bin for nb: NatBin
Explicit parameterization must be used systematically in order to guarantee future
flexibility in usage, especially for object types that contain binary methods.
21. A LANGUAGE wrrn MATCHING 309

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

CPoint ~ Object(X)[x: Int, c: Color, eq+: X~Bool, mv+: Int~Xl


These definitions freely use covariant and contravariant occurrences of Self types. The
liberal treatment of Self types in 0-3 yields CPoint <# Point. In 0-1, the same defini-
tions of Point and CPoint are valid, but they are less satisfactory because CPoint <: Point
fails; therefore the 0-1 definitions adopt a different type for eq. In 0-2, contravariant
occurrences of Self types are illegal; therefore the 0-2 definitions have a different type
foreq, too.
We define two classes pointClass and cPointClass that correspond to the types Point
and CPoint, respectively:
pointClass : Class(Point) ~
class with (self: X<#Point)
x=O,
eq = fun(other: X) self.x = other.x end,
mv = fun(dx: Int) selfx := self.x+dx end
end
cPointClass : Class(CPoint) !
subclass of pointClass: Class(Point)
with (self: X<#CPoint)
c = black
override
eq = fun(other: X) super.eq(other) and self.c = other.c end,
mv = fun(dx: Int) super.mv(dx).c := red end
end
The subclass cPointClass could have inherited both mv and eq. However, we chose to
override both of these methods in order to adapt them to deal with colors.
21. A LANGUAGE WITH MATCHING 311

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

Environments may contain both subtyping and matching assumptions:


Environments
(Env fIl) (Env X<:) (Env X<#) (Env x)
E f- A X~dom(E) E f- A :: Obj X~dom(E) E f- A x~dom(E)

E, X< :A f- 0 E, X<#A f- 0 E, x:A f- 0

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

(Type Class) (where A '" Object(X)[I,'\Ji:Bi iE1 .. n]) (Type AII<# )


E, X<#A I- Bj 'ltie1..n E, X<#A I- B
E I- Class(A) E I- All(X<#A)B

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

(Sub Invariant) (Sub Covariant) (Sub Contravariant)


EI-B E I- B <: B' VE {o,+} E I- B' <: B VE{O,-}
E I- vB <: + B' E I- vB <: -B'

Matching
(Match Refl) (Match Trans) (Match X)
E I- A:: Obj E I- A <# BEl- B <# C E', X<#A, E" I- 0

E I- A <# A EI- A <# C E', X<#A, E" I- X <# A

(Match Object) (Ii distinct)


E, X<:Top I- vjB j <: v ;' B;' 'r/ iEl .. n E, X<:Top I- Bj 'r/ iEn+ l..m
E I- Object(X)[Iivj:Bj jEl..n+m] < # Object(X)[l;tJ;':Bj' jE1..n]

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

El-a:B E', x:A , E" I- x : A

(Val Object) (where A == Object(X)[I,wBiIXI i<I.."))


E, x:A I- bj : Bj(AB 'r/iEl ..n
E I- object(x:A) Ij=bj iE 1..n end: A

(Val Select) (where A == Object(X)[I,wBiIXI iE I.."))


El-a : A' EI-A' < #A VjE{o,+} jEl..n
E I- a.lj : BjiA 'B

(Val Method Update) (where A == Object(X)[i.'ui:Bi <i I.."))


E I- a : A ' E I- A ' <# A E, x:A ' I- b : BjiA'B VjE{O,-} jEl..n
E I- a.Ij := method(x:A')b end : A '
314 PART III. HIGHER-ORDER CALCULI

(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)[lilJi:Bi iEJ..,+m), A' =Object(X')[I,'\J;':B;' iEJ..,), Ovr~1..n)

E I-- Class(A) E I-- c': Class(A') E I-- A <# A'


E, X<#A I-- B/ <: 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) /i=b i iEn+1..n+m override li=bi iEOVT end
: Class(A)

(Val Class Select) (where A = Object(X)[I,'\J;:Bi{X) i.1..,))


El--a:A' EI--A'<#A El--c:Class(A) jEl..n
E I-- c"lj(A',a) : Bj{A'}

(Val Fun<#) (Val Appl<#)


E, X<#AI--b: B E I-- b: All(X<#A)B/X} E I-- A' <# A
E I-- fun(X<#A) bend: All(X<#A)B E I-- b(A') : BHA'l

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.

21.5.1 Types and Operators


The central point of the translation of 0-3 into Ob_: 1l is in the treatment of object types.
In certain contexts, the object types of 0-3 are interpreted as types of Db_:1V while in
other contexts they are interpreted as operators of DbW<:Il'
In particular, Object(Y) ... is translated either as a type !l(Y) ... or as an operator
A,(Y) ... in different contexts. Two such contexts are the subtyping and the matching
judgments. A subtyping judgment ¢ r- A <: B of 0-3 is mapped to a subtyping judg-
ment ¢ r- d <: .8. of Ob_: 1V where d and .8. are types that correspond to A and B, respec-
tively. On the other hand, a matching judgment ¢ r- A <# B of 0-3 is mapped to a
higher-order subtyping judgment ¢ r- £1 -<: II of Obw<:1V where £1 and II are operators.
As in this example, we often use underlining to representthe results of the trans-
lation, with double underlining for operators. When both d and £1 occur in the same
context, they are different translations of the same type A, and arerelated by d == £1*.
In this dual translation process, the treatment of type variables is critical. Consider
an environment assumption X<#Object(Y)[ ... J in 0-3, which is mapped to an environ-
ment assumption X-<:A,(Y)[ ... Jin Obw<:1l' In the translation of the remainder of the judg-
ment, to the right of that assumption, the variable X may be subject to type or operator
translations. As an operator, it is translated unchanged as X; as a type it is turned into
its fixpoint X*.
For example, the judgment:
j1S, X<#Object(W)[lo:W], Y<#X, y:Yr- Object(Z)[lj :X, 12:Y, 13:Zj
is mapped to:
j1S, X-<:A,(W)[lo:W], Y-<:X, y:Y* r- (A,(Z)[lj:X*, 12:Y*' 13:Z])*
where both object types and variables receive different translations depending on their
context.
As another example, let £1 == A,(X)[I:Xj, so that d == £1* == /l(Y)(A,(X)[I:X])(Y) = /leX)
[I:X] is the translation of Object(X)[I:Xj. Consider the class type:
316 PART III. HIGHER-ORDER CALCULI

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.

21.5.2 Towards a Translation


The following tables are extracted from the translation presented in Section 21.5.3. Our
intent here is to summarize the translation of types and terms. These tables are not pre-
cise, but they should be useful for developing intuitions.
In this section, the symbol 3! means "informally translates to", with 3!Ty for trans-
lations that yield types, and 3!Op for translations that yield operators. We continue
using informal underlining conventions: we represent the translation of a term a by fL
the type translation of a type A by 6., and its operator translation by 6.. We say that a
variable X is subtype-bound when it is introduced as X<:A for some A; we say that X
is match-bound when it is introduced as X<#A for some A.
Translation summary
X 3!Op X (where X is match-bound in the environment)

X 3!Ty X (when X is subtype-bound in the environment)


X 3!Ty X* (when X is match-bound in the environment)
Top 3!Ty Top
Object(X)[IiVi:Bi iEl..n] 3!Ty (A(X)[Ii"Ui:lt iEl..n])*
Class(A) 3!Ty [new+:6., It:'V(X-<:6.)X*--t14 iEI.."]
where A :: Object(X)[IiVi:Bi iEr.."]
All(X<#A)B 3!Ty 'V(X-<:4)1~

X 3! X

object(x:A) li=b;lx\ iEI.." end 3! jold(.1,[I i=r;(x:4(A))Mfo1d(.1,x)) iEI.."])


a.lj 3! unjold(q).Ij
21. A LANGUAGE WITH MATCHING 317

a.lj := method(x:A')b\xl end ==


jold(A',unjold(q).ljfFr;(X:4,'(A'»lz(jold(A',x»»
newc == {;,.new
root == [new=r;(z:[new+:j.l(X)[lnfold(j.l(X)[],[])]
subclass of c':C' with(x:X<# A) h=bi ien+ I..n+m override li=bi ieOvr end ==
[new=r;(z:C)jold(.1,[ li=r;(S:~(.1) )Z.li(~)( fold{.1,s» iel ..n+mj)
li=r;(Z:C) ,'.Ii ie1..n-Ovr, - -
h=r;(Z:C)A(X -<:~)A(X:X*)Q; ieOvrvn+1..n+m]
where C :; Class(A)
cl\lj(A',a) == {;,.lj(4,')(q)
fun(X<#A)b end == A(K<:4,)lz
b(A' ) == lz(4,' )

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)

«E', X<:A, E"HX» ~ X


«E', X<#A, E"HX» ~ X*
«EHTop» ~ Top
«EHObject(X)[I;\Ji:Bi iEl..n)>> ~ (A.(X)[I;\Ji:«E, X<:TopHBi» i<l..n])*
«EHClass(A)>> ~
[new+:«EHA», It:V(X <::«EI'lA»)X*--1«E, X<#AHB i» iEl..n]
where A == Object(X)[litJi:Bi iEl .. n]
«EHAll(X<#A)B» ~ V(X<::«EI'lA»)«E, X<#AHB»

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»

Translation of 0-3 terms


21. A LANGUAGE WITH MATCHING 319

«EHobject(x:A) li=bi{x} iE1..n end» ~


jold(~[li=~(X:!1(A.»«E, X:AHM{Xf-jold(~xH iELn))
where A. ;: «E~A» and 4 ;: «EOIA»
«EHa.lj » ~ unjold(<<EHa»).lj
«EHa.lj := method(x:A')b{x} end» ~
jold(A. ',unjold(«EHa»).lj~~(x:!1 '(£l '»<<E, X:A'Hb»{xf-jold(£l',x)B)
where A';: «EHA'» and 4' ~ «EOIA'»
«EHnew c» ~ «EHc».new
«EHroot» ~ [new=~(z:[new+:~(X)[)Dfold(~(X)[],[))]

«EHsubclass of c':C' with(x:X<#A) li=bi iEn+1..n+m override li=b i iEOvr end» ~


[new=~(z:C)jold(~[IF~(s:!1(A.»z.li(!1)(jold(~s» iE1..n+m])
li=~(Z:C) «EHc'».li iELn-Ov': -
IF~(Z:C)A(X-< :!1)A(X:X*)«E, X<#A, x:XHM iEOvrvn+Ln+m]
where C;: «EHClass(A)>>, Li;: «EHA», 4;: «EOIA», and zl!dom(E)u{s}
«EHc"lj(A',a)>> ~ «EHc».lj(<<EOIA'»)(<<EHa»)
«EHfun(X<#A)b end» ~ A(X . <:«EOIA»)«E, X<#AHb»
«EHb(A')>> ~ «EHb»(<<EOIA'»)

Translation of 0-3 judgments


I
«E I- <>>> ~ «E» I- <>

«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»

21.5.4 Soundness of the Translation


The following lemmas describe some relationships between the translations of types
and environments.
Lemma 21.5-1 (Environment suffix)
If E, E' I- <> in 0-3, then «E, E'» ;: «E», «EHE '».
o
320 PART III. HIGHER-ORDER CALCULI

Lemma 21.5-2 (Relationship between type and operator translations)


If E f- A:: Obj in 0-3, then «EHA» =«EIoIA»*.
o
Lemma 21.5-3 (Weakening)
(1) If E f- A and E, E' f- 0 in 0-3, then «EHA» =«E, E'HA».
(2) If E f- A :: Obj and E, E' f- 0 in 0-3, then «EIoIA» = «E, E'IoIA».
o
Lemma 21.5-4 (Substitution)
(1) If E, X<:Top, E' f- Band E f- A' in 0-3,
then «E, X<:Top, E'HB»fXt-«EHA'»} = «E, E'(Xt-A'BHBiXt-A')>>.
(2) If E, X<#A, E' f- Band E f- A':: Obj in 0-3,
=
then «E, X<#A, E'HB»HXt-«EIoIA'»B «E, E'{Xt-A'}HB(Xt-A'B».
(3) If E, X<:Top, E' f- B:: Obj and E f- A' in 0-3,
then «E, X<:Top, E'IoIB»iXt-«EHA'»B =«E, E'{Xt-N}IoIBIXt-A'I».
(4) If E, X<#A, E' f- B :: Obj and E f- A':: Obj in 0-3,
then «E, X<#A, E'IoIB»!Xt-«EIoIA'». =«E, E'{Xt-A'JIoIBIXt-A'B».
o
The following theorem states that the translation of 0-3 preserves validity of judg-
ments. As a consequence, the type rules of 0-3 are sound.
Theorem 21.5-5
The translation of a valid judgment of 0-3 is a valid judgment of Db_:w
Proof
Assume ~ is a valid judgment of 0-3, with a given derivation tree. We show that
«~» is a valid judgment of Db_:11 by induction on the length of the derivation of~.

Cases (Env iii), (Env X<:), (Env X<#), (Env x)


Easy.
Case (Type Obj)
We have ~ = E f- A, obtained from E f- A :: Obj. By induction hypothesis, «E» f-
«EIoIA» :: Op is derivable in Db_:Il. Therefore «E» f- «EIoIA»* is derivable as well; this
is the translation of ~ by Lemma 21.5-2.
Cases (Type X), (Type Top)
Easy.
Case (Type Class)
= =
We have ~ E f- Class(A), where A Object(X)[litJi:Bi if!..n); this judgment is
obtained from E, X<#A f- Bi for iEl..n. By induction hypothesis, the judgments «E,
21. A LANGUAGE WITH MATCHING 321

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).

Case (Match Object), (Val Subsumption)


Easy.
Case (Val x)
We have t) == E', x:A, E" f- x: A, obtained from E', x:A, E" f- o. By induction hypoth-
esis, «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 (Val x) we obtain «E'», X:«E'HA», «E', X:AHE"» f-
x: «E'HA». 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).
Case (Val Object)
We have t) == E f- object(x:A) li=bilx} iE!..n end: A, where A == Object(X)[li'Ui:BiIX}
iE1..n], obtained from E, x:A f- bilx} : BiKA» for iEl..n. By induction hypothesis, the
judgments «E», X:«EHA» f- «E, X:AHMlx} : «E, x:AHBiiA»» for iEl..n are derivable in
Obox:1l" These can be simplified to «E», x:«E .... A» f- «E, X:AHb;»lx} : «EHBiiA»».
We have «E .... A» == «E~A»* == I1(X)«E~A»(X) by Lemma 21.5-2, and «E~A»(<<EHA») ==
(«E~A»(X)){Xf-<<E""A»». From these equalities we obtain «E», x':«E~A»(<<EHA») f-
!old(<<E .... A»,x'): «EHA». By weakening «E», X:«EHA» f- «E, X:AHMlx} : «E~BiiA»», we
322 PART III. HIGHER-ORDER CALCULI

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

Case (Val Root)


Easy.
Case (Val Subclass)
We have ~ == E f- subclass of c':Class(A') with(x:X<#A) li=bi iEn+l .. n+m override li=bi
iEOvr end: Class(A), obtained from a number of hypotheses, which we will invoke
in the course of the argument. First we set: 1 ~ «E~A» == A(X)[I,w«E,
X<:TopHBi»IX} iE1..n+m], l' ~ «E~A'» == A(X)[liu;':«E-;-X<:TopHB;'»!X} iE1..n], A ~
«EHA», A' ~ «EHA'», ~ ~ «E>-<Class(A)>> == [new+:£l, 1/:V'(X-<:1)X*-7«E, X<#AHBi»IX}
iEl .. n+m], and ~' ~ «E>-<Class(A')>> == [new+:A', 1/:V'(X-<:1')X*-7«E, X<#A'>-<B;'»IX}
iE1..n]. By Lemma 2l.5-2, A == 4* and A' == 4'*. -
By induction hypothesis, for iEOvrvn+ l..n+m, «E, X<#A, x:X f- bi : Bi» == «E», X-<:1,
x:X* f- «E, X<#A, x:X>-<b i» : «E, X<#A, X:X>-<Bi» is derivable in Obw<:~ hence «E»j:..
A(X -<:4)A(X:X*)«E, X<# A, x:X>-<b;» : V'(X -<:4)X* -7«E, X<# A>-<Bi» is derivable as well.
By induction hypothesis, for iEl..n-Ovr, «E» f- 1 -<: l' and «E», X-<:1 f- «E,
X<#A>-<B;'» -<: «E, X<#A>-<B i» are derivable in Obw<=Iu -and hence so is «E» I-
V'(X -< :4)X*-+«E, X<#A>-<Bi'» <: V'(X-<:4)X*-+«E, X<#A>-<Bi»·
Moreover, also by induction hypothesis, «E» f- «E>-<c'» : ~'is derivable in ObW<:/l' We
obtain «E» f- «E>-<c'».li : V'(X-<:4)X*-7«E, X<#A>-<Bi» by (Val Select) and subsumption.
Using the definition of .c. and Lemma 2l.5-2 we can prove that «E», z:C s:1(£1) I-
z.li(1)(fold(A,s)) : «E, X<#A>-<B i»RXf-1B; this is «E», z:C s:1(A) f- z.IM)(fold(£l,s)) :
«E>-<Bi(AB» by Lemma 2l.5-4(2). Hence, much as in the a..se of (Va! Object), we
obtain «E», z:~ f- [li=~(s:4(A))zJi(4)(fold(£l,s)) iE1..n+m] : 4(£1).
Combining these intermediate results, and using Lemma 21.5-2, we obtain that «E»
f- [new=~(z:~)fold(A,[li=~(s:1(A))z.li(1)(fold(£l,s)) iE1..n+mJ), li=~(z:O«E>-<c'»./i iELn-Ovr
li=~(z:~)A(X-<:1)A.(x:X*)«E,)C<#A, X~X>-<bi» iEOvrvn+1..n+m] : ~ is derivable in Obw<:w
This is the tra~lation of ~.
Case (Val Class Select)
We have ~ == E f- cl\/j(A',a) : BjRA'B, obtained from E f- a : A', E f- A' <# A, and E I- c
: Class(A), with A == Object(X)[I,'Ui:BiIX} iE1..n]. By induction hypothesis, «E» f- «E>-<a»
: «E>-<A'», «E» f- «E>-<A'» -<: «E>-<A», and «E» f- «E>-<c» : «E>-<Class(A)>> are derivable in
ObO)<:", where «E>-<Class(A)>> == [new+:«E>-<A», It :V'(X-<:«E~A»)X*-7«E, X<#A>-<B i»
iE1..n]. We obtain «E» f- «E>-<c».lj : V'(X-<:«E~A»)X*-7«E, X<#A>-<B j», hence «E» f-
«E>-<c».Ii<<<E~A'»): «E~A'»*-+«E, X<#A>-<Bj»{Xf-«E~A'n By Lemma 2l.5-4(2), this is
the same as «E» I- «E>-<c».lj(<<E~A'») : «E~A'»*-7«E>-<BjRXf-A'»» . By Lemma 21.5-2,
«E>-<A'» == «E~A'»*, hence «E» f- «E>-<c».lj(<<E~A'»)(<<E>-<a») : «E>-<Bj{Xf-A'B», which is
the translation of ~.
Case (Val Fun<#)
Easy.
324 PART III. HIGHER-ORDER CALCULI

Case (Val Appl <#)


We have t):; E I- b(A'): BfA'D, obtained from E I- b: All(X<#A)B{XI and E I- A' <#
A. By induction hypothesis, «E» I- «EHb» : If(X-<:«EI'IA»)«E, X<#AHB» and «E» I-
«EI'IA'» -<: «EI'IA» are derivable in Dbw<:w Therefore «E» I- «EHb»(<<EI'IA'») : «E,
X<#AHB»{Xf--«EI'IA'»& is derivable as well. By Lemma 21.5-4(2), this judgment is
«E» I- «EHb»(<<EI'IA'») : «EHBCXf--A'D», the translation of t).
o
Epilogue
At the start of this book we reviewed some of the central concepts in object-oriented
programming. We hope to have clarified them in the course of Parts I, II, and III. In this
Epilogue, we summarize our main conclusions.
The setting for our work has been a family of object calculi. These calculi have a
tiny syntax with few orthogonal constructs, but they are remarkably expressive. The
calculi have both functional and imperative operational semantics; these semantics cor-
respond well to the behavior of common languages, yet are simple enough for use in
proofs. In these respects, object calculi are analogous to A-calculi. Unlike A-calculi, how-
ever, object calculi are patently object-oriented, and enable us to express common
object-oriented examples directly and naturally.
In our calculi, objects are primitive but classes are not. Classes can be encoded in
terms of objects, easily and faithfully. The encoding is consistent with our typing rules.
In fact, the subtyping rules serve in justifying inheritance, even in situations where
inheritance is decoupled from subtyping (for example, in the presence of binary meth-
ods). Moreover, the encoding of classes lends itself to several variants; for instance, it
can account for protected and private methods. As a result of this encoding, we obtain
a satisfactory reduction of class-based languages to the simpler object-based lan-
guages. To our knowledge, such a reduction had never been defined precisely, despite
the existence of many suggestive programming techniques and examples.
In addition to the encoding of classes, we found a translation from A-calculi to
object calculi that gives a simple method for expressing functions as objects. Thus we
were able to reduce procedural languages to object-based languages. This reduction
lends weight to the informal thesis that "everything is an object", which is at the center
of the Smalltalk philosophy. To our knowledge, this reduction had never been defined
precisely either.
We have also considered the converse reduction, both by giving a denotational
model for objects and by giving a translation of objects into a standard A-calculus. In
these interpretations, objects are records and methods are functions. The semantics of
object types combines some intuitive ingredients (recursion, data abstraction) but is
rather intricate, essentially because of the difficulty in accounting for object subtyping.
Therefore the interpretations do not provide practical reductions of object-oriented
programming to procedural programming. We conclude that it is best to accept objects
as primitive, rather than try to explain them away.
In developing our calculi, we put emphasis on their typing rules and on the prob-
lem of type safety. One reason for this emphasis is that types serve to organize our
thinking about objects. Another reason is that peculiar type systems are an important
part of many object-oriented languages, and that these type systems are often unsafe.

M. Abadi et al., A Theory of Objects


© Springer Science+Business Media New York 1996
326 A THEORY OF OBJECTS

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

A.l Simple-Objects Fragments


These are the typing and equational rules for simple objects.

dObU~:Ob

(Type Object) (Ii distinct)


E I- Bi lfiEl..n
E I- [li:Bi iEl..n]

(Sub Object) (Ii distinct)


E I- Bi lfiEl..n+m

{Val Object) (where A;: [li:Bi iELn))


E, Xi:A I- bi: Bi lfiEl..n
E I- [li=C;(xi:A)bi iEl..n] : A

(Val Select) (Val Update) (where A;: [li:Bi iELn))


EI-a: [li:BiiEl..nj jEl..n El-a:A E,x:Al-b:Bj jEl..n
E I- a.lj : Bj E I- a.lj~c;(x:A)b : A

(Eq Object) (where A ;: [li:Bi iELnJ)


E, Xi:A I- bi H bi' : Bi lfiE l..n

(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

(Eq Select) (Eq Update) (where A'" [li:Bi i<l..n])


E I- a H a': [li :Bi it!.."] jEl..n E I- a H a': A E, x:A I- b H b' : Bj jEl..n
E I- a.lj H a'.lj : Bj E I- a.lj~c;(x:A)b H a'.lj~c;(x:A)b' : A

(Eval Select) (where A'" [li:Bi i<l..n), a '" [IF~(xi:A')bilxil i<l..n+m])


E I- a : A jE l..n
E I- a.lj H bj{al : Bj

(Eval Update) (where A'" (li:Bi i<l..n), a '" (li=~(xi:A')bi it1..n+m))


E I- a : A E, x:A I- b : Bj jE l..n
E I- a.lj~c;(x:A)b H [lj=C;(x:A')b, li=C;(x;:A ')b i i<(1 .. n+m Hj ,] : A

A.2 Other Typing Fragments


These are other typing fragments for first-order and second-order calculi.
6.x
(Envgl) (Env x) (Valx)
E I- A x;.dom(E) E', x:A, En I- 0

r61-0 E, x:A I- 0 E', x:A, En I- X : A

(Type Const)
EI-o
EI-K

(Type Arrow) (Val Fun) (Val Appl)


EI-A EI-B E, x:A I- b : B E I- b : A-.+B E I- a : A
EI-A-.+B E I- A.(x:A)b : A-.+B E I- b(a): B

6.<:

(Sub Refl) (Sub Trans) (Val Subsumption)


EI-A E I- A < : BEl- B <: C E I- a : A E I- A <: B
EI-A <: A EI- A <: C El-a:B
A. FRAGMENTS 331

(Type Top) (Sub Top)


Ef--o EI-A
E I- Top EI- A <: Top

(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

(Env X<:) (Type X<:) (Sub X)


E I- A X~dom(E) E', X<:A, E" I- 0 E', X<:A, E" I- 0

E, X<:A I- 0 E', X<:A, E" I- X E', X<:A, E" I- X <: A

(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

(Type Rec<:) (Sub Rec)


E, X<:Top I- A E I- Il(X)A E I- Il(Y)B E, Y<:Top, X<:Y I- A <: B
E I-Il(X)A E I- Il(X)A <: Il(Y)B
332 ApPENDIX. RULES AND PROOFS

(Val Fold) (where A == I1(X)B{X}) (Val Unfold) (where A == ~X)B{X})


EI-b: BlAH Ef-a : A
E f- fold(A,b) : A E f- unfold(a) : Bf A}

(Type All) (Val Fun2) (Val Appl2)


E, X f- B E, X f- b : B E f- b : 'V(X)B{X} E f- A
E f- 'V(X)B E f- A(X)b : 'V(X)B E f- b(A) : BIA}

~:V

(Type All<:) (Sub All)


E, X< :A f- B E f- A ' < : A E, X< :A' f- B <: B'
E f- 'V(X<:A)B E f- 'V(X<:A)B <: 'V(X<:A')B'

(Val Fun2<:) (Val AppI2<:)


E, X<:A f- b : B E f- b: 'V(X<:A)B{X} E f- A ' < : A
E f- A(X<:A)b: 'V(X<:A)B E f- b(A') : BiA"

(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

(Type Exists<:) (Sub Exists)


E, X<:A f- B E f- A <: A ' E, X<:A f- B <: B'
E f- 3(X<:A)B E f- 3(X<:A)B <: 3(X<:A')B'
A. FRAGMENTS 333

(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

A.3 Other Equational Fragments


These are other equational fragments for first-order and second-order calculi.

!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

E', x:A, E" f- x H x :A

(EqFun) (Eq App))


E, x:A f- b H b': B E f- b H b' : A-7B 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

(Eval Beta) (Eva! Eta)


E f- A(x:A)b{x} : A-7B E f- a : A E f- b: A~B x¢.dom(E)
E f- (A(x:A)b{x})(a) H b{ai : B E f- A(x:A)b(x) H b: A-7B

(Eq Subsumption) (EqTop)


E f- a H a': A E f- A <: B Ef-a:A Ef-b : B
Ef-a Ha': B E f-a H b: Top
334 ApPENDIX. RULES AND PROOFS

(Eq Fold) (where A == I1(X)B{X}) (Eq Unfold) (where A == I1(X)B{X})


E f-b H b' : BiAl Ef-aHa': A
E f- fo1d(A,b) H fo1d(A,b') : A E f- unfold(a) H unfold(a') : BiAl

(Eva! Fold) (where A == I1(X)B{X}) (Eval Unfold) (where A == I1(X)B{X})


Ef-a:A Ef-b: BiAl
E f- fold(A,unfold(a)) H a:A E f- unfold(fold(A,b)) H b: BiAj

(EqFun2) (Eq Appl2)


E, X f- b H b': B E f- b H b' : V(X)BIX} E f- A
E f- A(X)b H A(X)b' : V(X)B E f- b(A) H b'(A) : BiAH

(Eval Beta2) (Eval Eta2)


E f- A(X)bIX} : V(X)BIX} E f- A E f- b : V(X)B Xtdom(E)
E f- (A(X)bIX})(A) H blAH: BiAH E f- A(X)b(X) H b : V(X)B

(Eq Fun2<:) (Eq AppI2<:)


E, X<:A f- b H b' : B E f- b H b': V(X<:A)BIX} E f- A' <: A
E f- A(X<:A)b H A(X<:A)b' : V(X<:A)B E f- b(A') H b'(A') : BIA'j

(Eval Beta2<:) (Eval Eta2<:)


E f- A(X<:A)bIX} : V(X<:A)BIX} E f- C <: A E f- b : V(X<:A)B Xtdom(E)
E f- (A(X<:A)bIX})(C) H bie! : B{C» E f- A(X<:A)b(X) H b : V(X<:A)B

(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! Unpack<:) (where c '" pack X=C with b{X}:B{X})


E I- c : 3(X)B{X} E I- D E, X, x:B{X} I- d{X,x} : D
E I- open cas X,x:B{X} in d{X,x} :D H d{C,bK»» : D

(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

B.t The Ob1<: Calculus


This is the first-order calculus with objects and subtyping.
Syntax of the Ob 1<: calculus
A,B ::= types
K ground type
Top the biggest type
[li: Bi iEl..n) object type (Ii distinct)
a,b ::= terms
x variable
[li=~(xi:Ai)bi iEl..n) object (h distinct)
a.l method invocation
a . I~~(x : A)b method update

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

(Type Const) (Type Top) (Type Object) (Ii distinct)


£1--0 EI-o E I- Bi 'v'iEl .. n
EI-K E I- Top E I- [lj:Bj iEl..n)

(Sub Refl) (Sub Trans)


EI-A E I- A < : B E I- B < : C
EI- A <: A EI- A <: C
338 ApPENDIX. RULES AND PROOFS

(Sub Top) (Sub Object) (Ii distinct)


EI-A E I- Bj 'v'iEl..n+m
EI- A <: Top E I- [lj:B j iel..n+m] <: [li:Bj iel..n]

(Val Subsumption) (Val x)


E I- a : A E I- A <: B E', x:A, En I- 0

El-a:B E', x:A, En I- X : A

(Val Object) (where A'" [li:Bj jE1..n])


E, Xi:A I- bi: Bj 'v'iEl..n
E I- [li=~(xi:A)bi iel..n] : A

(Val Select) (Val Update) (where A'" [/i:Bi iel..n])


EI-a: [lj:Bjiel.. n ] jEl..n E I- a: A E, x:A I- b : Bj jEl..n
E I- a.lj: Bj E I- a.lj*~(x:A)b : A

(EqSymm) (EqTrans)
El-aHb:A El-aHb:A El-bHC:A
El-bHa:A El-aHc : A

(Eq Subsumption) (Eq Top) (Eqx)


E I- a H a' : A E I- A <: B El-a : A El-b : B E', x:A, En I- 0

EI-a Ha': B E I-a H b: Top E', x:A, En I- X H X : A

(Eq Object) (where A'" [/i:Bi iE1..n])


E, Xi:A I- bi H b;' : Bi 'v'iEl..n
E I- [lj=~(xj:A)bj ie1..n] H [lj=~(xj: A)b;' jel..n] : 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

(Eq Select) (Eq Update) (where A '" [/j:Bi iEl..n])


E I- a H a' : [lj:B j je l..n] jE l..n E I- a H a' : A E, x :A I- b H b' : Bj jE l..n
E I- a.lj H a'.lj : Bj E I- a.lj*~(x:A)b H a'.lj*~(x:A)b' : A
B. SYSTEMS 339

(Eval Select) (where A == [li:Bi iEl..n], a == [li=<;(xi: A ' )b;/xil iEl..n+m))

El-a : A jEl..n
E I- a.lj H bj{aJ : Bj

(Eval Update) (where A == [li:Bi iEl..nj, a == [li=<;(xi:A ' )bi iE I . n+m))


.

El-a:A E,x:Al-b:Bj jEl..n


E I- a.lj~~(x:A)b H [IF~(x:A ')b, li=~(xi:A ')bi iE(l..n+mHjl] : A

B.2 The F<:11 Calculus


This is the second-order calculus with subtyping and recursion. Objects may be added,
using the same rules as for Ob1<: (see also Section 13.2).
Syntax of the F<:11 calculus
A,B,C,D::= types
X type variable
Top the biggest type
A-7B function type
J.l.(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
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

The rules are grouped by judgment.

(Env¢) (Env x) (Env X<:)


E I- A x~dom(E) E I- A X~dom(E)

E, x:A I- 0 E, X< :A I- 0
340 ApPENDIX. RULES AND PROOFS

(TypeX<:) (Type Top) (Type Arrow)


E', X< :A, En f- <> Ef-o Ef-A Ef-B
E', X< :A, En f- X Ef-Top Ef-A-"7B

(Type Rec<:) (Type All<:) (Type Exists<:)


E, X<:Topf-A E,X<:Af-B E,X<:Af- B
E f- Il(X)A E f- 'v'(X<:A)B E f- 3(X<:A)B

(Sub Refl) (Sub Trans)


Ef-A E f- A <: B E f- B <: C
Ef-A<:A Ef-A<:C

(Sub X) (Sub Top) (Sub Arrow)


E', X< :A, En r 0 Ef-A E f- A' <: A E f- B < : B'
E', X< :A, En f- X <: A Ef-A<: Top E f- A--7B <: A'--7B'

(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

(Sub All) (Sub Exists)


Ef- A' <: A E, X<:A'f- B <: B' E f- A < : A' E, X< :A f- B <: B'
E f- 'v'(X<:A)B <: 'v'(X<:A ' )B' E f- 3(X<:A)B < : 3(X<:A')B'

(Val Subsurnption) (Val x)


E f- a : A E f- A <: B E', x:A, En f- 0

Ef-a: B E', x :A, En f- X : A

(Val Fun) (Val App!)


E, x:A f- b: B E f- b : A--7B Era : A
E f- A(x:A)b : A-"7B E f- b(a) : 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 Fun2<:) (Val AppI2<:)


E, X<:A f- b: B E f- b: V(X<:A)B{X} E f- A' <: A
E f- A.(X<:A)b: V(X< :A)B E f- b(A') : BHA'R

(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

(Eq Syrnrn) (Eq Trans)


Ef-aHb:A Ef-aHb:A Ef-bHC : A
Ef-aHc : A

(Eq Subsurnption) (Eq Top) (Eqx)


E f- a H a' : A E f- A <: B E f- a: A E f- b : B E', x:A, En f- 0

Ef-a Ha': B E f- a Hb : Top E', x:A, En f- X H x: A

(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

(Eq Fun2<:) (Eq AppI2<:)


E, X<:A f- b H b': B E f- b H b' : V(X<:A)B{X} E f- A' <: A
E f- A(X<:A)b H A.(X<:A)b': V(X<:A)B E f- b(A') H b'(A'): BfA'»

(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! Beta) (Eval Eta)


E I- A.(x:A)b{x} : A~B E I- a : A E I- b : A~B x;'dom(E)
E I- (A.(x:A)b{x})(a) H b{a) : B E I- A.(x:A)b(x) H b : A~B

(Eva! Fold) (where A .. I1(X)B\XJ) (Eva! Unfold) (where A .. I1(X)BIXJ)


El-a:A E I- b: BIA)
E I- fold(A,unfold(a» H a : A E I- unfold(fold(A,b» H b : BlA,

(Eval Beta2<:) (Eval Eta2<:)


E I- A.(X<:A)b{X} : 'V(X<:A)B{X} E I- C <: A E I- b : 'V(X<:A)B X;'dom(E)
E I- (A.(X<:A)bIX})(C) H ble} : BiC) E I- A.(X<:A)b(X) H b : 'V(X<:A)B

(Eval Unpack<:) (where c .. pack X<:A=C with b\X}:B\XJ)


E I- c: 3(X<:A)B{X} E I- D E, X<:A, x:B{X} I- d{X,x} : D
E I- open cas X<:A,x:BIXI in dIX,xl:D H dIC,bICJJ : 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

B.3 The C;Ob Calculus


This is the second-order calculus with object types and the Self quantifier.
Syntax of the ~Ob calculus
A,B::= types
X type variable
Top the biggest type
[li:Bi ie1..nj object type (Ii distinct)
C;(X)B Self quantified type
a,b::= terms
x variable
[li=C;(xi:Ai)b i ie1..nj object (Ii distinct)
B. SYSTEMS 343

a.1 method invocation


a./~r;(x)b method update
wrap(X<:A=B)b wrap
use a as X<:A, y:B in b:D use

The rules are grouped by judgment.

(Env¢) (Env x) (Env X<:)


E I- A x~dom(E) E I- A X~dom(E)

E, x:A I- 0 E, X<:A I- 0

(TypeX<:) (Type Top)


E', X<:A, E" I- 0 EI-o
E', X< :A, E" I- X E I- Top

(Type Object) (Ii distinct) (Type Self)


E I- Bi V'iEl..n E, X<:Top I- B
E I- [li:Bi iE1..nj E I- C;(X)B

(Sub Refl) (Sub Trans)


EI-A E I- A <: BEl- B <: C
E I- A <: A E I- A <: C

(Sub X) (Sub Top)


E', X<:A, E" I- 0 E I- A
E', X<:A, E" I- X <: A E I- A <: Top

(Sub Object) (Ii distinct) (Sub Self)


E I- Bi V'iEl..n+m E, X<:Top I- B <: B'
E I- [li:Bi iEl..n+mj <: [li: Bi iE1..nj E I- C;(X)B <: C;(X)B'

(Val Subsumption) (VaIx)


E I- a : A E I- A <: B E', x:A, E" I- 0

El-a:B E', x:A, E" I- x: A


344 ApPENDIX. RULES AND PROOFS

(Val Object) (where A'" [/i:Bi i.l..n])


E, Xi:A f- bi : Bi 'v'iEl..n
E f- [li=c,;(Xi:A)b i if!..n] : A

(Val Select) (Val Update) (where A'" [li:Bi i.l..n])


E f- a: [li:Bi if!..n] jEl .. n Ef-a:A E,x:Af-b:B j jEl .. n
E f- a.lj : Bj E f- a.lj~c,;(x:A)b : A

(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

(EqSymm) (Eq Trans)


Ef-aHb:A Ef-aHb:A Ef-bHc:A
Ef-bHa:A Ef-aHc:A

(Eq Subsumption) (Eq Top) (Eq x)


E f- a H a': A E f- A <: B Ef-a : A Ef-b:B E', x:A, E" f- ~

Ef-aHa': B Ef-a H b: Top E', x:A, E" f- x H x: A

(Eq Object) (where A'" [/i:Bi i.l..n])


E, Xi:A f- bi H b/ : Bi 'v'iEl..n

(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

(Eq Select) (Eq Update)


(where A '" [/i:Bi i.l..n])
E f- a H a': [li:Bi ie1..n] jEl .. n Ef-aHa':A E,x:Af-bHb':Bj jEl..n
E f- a.lj H a'.lj : Bj E f- a.lj~c,;(x:A)b H a'.lj~c,;(x:A)b' : A

(Eq Wrap) (where A;: C;(X)B{XI, A';: 9X)B'{X))


E f- C <: A' E f- A E, Y f- B'{Y& <: BlY} E f- biC} H b'ie} : BK}
E f- wrap(Y<:A=C)b{Yj H wrap(Y<:A'=C)b'{Yj : A
B. SYSTEMS 34S

(Eq Use) (where A;: 9X)B/X))


E I- C H c': A E I- D E, Y<:A, y:BHYH I- d H d': D
E I- use C as Y<:A, y:B(Y} in d:D H use c' as Y<:A, y:BHY} in d':D : D

(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

(Eval Unwrap) (where A;: C;(X)B/X), c;: wrap(Z<:A=C)b/Z))


E I- c: A E I- D E, Y<:A, y:BiY} I- d{Y,y} : D
E I- use c as Y<:A, y:B{YJ in d{Y,y} :D H dK,biO» : D

(Eval Rewrap) (where A;: 9X)B/X))


E I- b : A E, y:A I- d{y} : D
E I- use b as Y<:A, y:B{YJ in 4Hwrap(Y'<:A= Y)yB :D H dHbB : D
c PROOFS

C.l Proof of the Variance Lemma from Section 13.3


Our goal in this appendix is to prove technical lemmas that guarantee that positive
occurrence is a sufficient condition for covariance. As a first step, we state two lemmas.
Lemma C.l-t (Bound weakening)
If E, X<:A, E' I- ~ and E I- A' <: A, then E, X<:A', E' I-~ .
o
Lemma C.l-2 (Substitution)
If E, X<:A, E'{X) I- ~(X) and E I- A ' < : A, then E, E'{A'& I- ~(Al

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»

In order to manipulate positive and negative occurrences of a variable, we prove a


general lemma about the separation of a set of occurrences into two sets:
Lemma C.l-3 (Variable separation)
(1) Let Y and Z be different variables that may occur free in an environment E'{Y,Z).
Assume XiFV(E') and Y,Udom(E, E').
If E, X<:C, E'HX,X) I- 0, then E, Y<:C, Z<:C, E' {Y,Z) I- o.
(2) Let Yand Z be different variables that may occur free in an environment E'{Y,Z)
and in a type DIY,Z) . Assume XiFV(E')vFV(D), and Y,Udom(E, E').
If E, X<:C, E'{X,X& I- DgX,X}, then E, Y<:c, Z<:C, E'{Y,Z) I- DIY,Z) .
Proof
The proof is by simultaneous induction on the derivations:
(1)

Case (Env jIJ)


Not applicable.
348 ApPENDIX. RULES AND PROOFS

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

W<:Top f- D1KX,Xj. By induction hypothesis (2), E, Y<:C, Z<:C, E'{Y,Zj, W<:Top f-


DIIY,Z}. By (Type Rec<:), E, Y<:c, Z<:C, E'{Y,Z} f- ~(W)(Dl{Y'Z)).
Case (Type All<:)
In this case, DHX,X} == V(W<:D')D" with Wtdom(E)v{X, Y,Z}. Hence D{Y,Z} ==
V(W<:Dl{Y,Z))D2{Y,Z} for some Dl and D2 such that D' == DIKX,Xj and D" ==
D2KX,Xj. We have E, X<:c, £,«X,X} f- DIKX,Xj and E, X<:C, E'{X,X}, W<:Dl{X,X} f-
D2KX,Xj. By induction hypothesis (2), E, Y<:C, Z<:C, E'{Y,Z} f- Dl{Y'Z} and E, Y<:C,
Z<:C, E'{Y,Z}, W<:Dl{Y'Z} f- D2{Y'Z}. By (Type All<:), E, Y<:C, Z<:C, E'{Y,Z} f-
V(W<:D1{Y,Z)) D 2 {Y,Z} .
Case (Type Exists<:)
Similar to case (Type All<:).
o
Corollary C.1-4
Under the same assumptions, we can also conclude:
(1) E, Y<:C, Z<: Y, E'{Y,Z} f- 0

(2) E, Y<:C, Z<:Y, E'{Y,Z} f- D{Y,Z}


Proof
By Lemma C.l-l, since E, Y<:C f- Y <: C.
o
The following general Variance Lemma establishes that positive occurrence is a
sufficient condition for covariance, and negative occurrence for contravariance.
Lemma C.1-5 (Variance)
Assume E'{V+,U-} and A{V+,U-} with ytdom(E, X<:C, E') and XtFV(E')vFV(A).
If E, X<:c, E'iX,X} f- AKX,X}, then E, X<:C, Y<:X, E'KY,X) f- AHY,X} <: AHX, Yl.
Proof
The proof is by induction on the size of A{V+,U-}. Without loss of generality, we
assume that U and V are different from all the variables bound in A.
CaseA{V+,U-} == W
If W == V, then AKY,X} == Yand A(X,y} == X. We have E, X<:C, E'iX,X} f- <>, so E,
U<:C, V<:U, E'{V+,U-} f- 0 by Corollary C.1-4(1), and E, X<:c, Y<:X, E'IY,X} f- 0 by
renaming. Then E, X<:C, Y<:X, E'{Y,X} f- Y <: Xby (Sub X).
IfW¢ V, thenAHY,X} == W== AHX,Y} and WEdom(E, E'). We have E, X<:C, E'HX,XJ
f- <>, so E, U<:C, V<:U, E'{V+,U-} f- 0 by Corollary C.l-4(l), and E, X<:C, Y<:X,
E'HY,X} f- 0 by renaming. Then E, X<:C, Y<:X, E'IY,X} f- Wby (Type X<:), and E,
X<:C, Y<:X, E'{Y,XH f- W <: Wby (Sub Refl).
350 ApPENDIX. RULES AND PROOFS

Case A(V+,l.r} == Top


Then A(Y,XB == Top == A«X, n. We have E, X<:C, E'{X,XD I- <>, so E, U<:C, V< :U,
E' /V'",U-} I- <> by Corollary C.1-4(1), and E, X<:C, Y<:X, E'(Y,XB I- <> by renaming.
Then E, X<:C, Y<:X, E'{Y,X} I- Top by (Type Top), and E, X<:C, Y<:X, E'( Y,XB I- Top
<: Top by (Sub Ref!).
Case A(V+,lll == [l/:A/ iEI.. n]
Then U and V cannot occur in any of the Ai. We have E, X<:C, E'{X,X} I- Ai. Hence
E, U<:C, V< :U, E'/V'",U-} I- Ai by Corollary C.1-4(2), and E, X<:C, Y< :X, E'C Y,X} I-
Ai by renaming. Then E, X<:C, Y< :X, E'{Y,XB I- A <: A by (Sub Object).
Case A(V+,lll == A 1(tr, V-l~A2(V+,lll
We have E, X<:C, E'{X,XB I- AleX,X) and E, X<:C, E'(X,XB I- A2{X,Xl To apply the
induction hypothesis we need to consider A'l ! AI(V,U!, so A'dV'",U-} and
A'I{D',D"} == AIID',D"B for any D' and D". We have E, X< :C, E'(X,X} I- A'I{X,Xl
By induction hypothesis, E, X< :C, Y< :X, E'{Y,X! I- A'ICY,X} <: A 'I{X, Yi; that is, E,
X< :C, Y< :X, E'(Y,X! I- AI{Y,X, <: AI{X,Yl Also by induction hypothesis, E, X<:C,
Y<:X, E'(Y,X, I- A2IY,XB <: A2{X,Y}. By (Sub Arrow), E, X< :C, Y<:X, E'{Y,X} I-
AleX, YB~A2{Y,XB <: AI(Y,XB~A2{X,n That is, E, X<:C, Y<:X, E'{Y,XB I-
(At-+A2){Y,XB <: (A t -+A2){X, Y).
Case A{V+,lll == (J.1(Z)AI(ZlHv+,lll
Then either V and U do not occur free in AI/Zl, or At/Z+)(V'",U-}, by definition of
variant occurrence.
In the former case, we have E, X<:C, E'(X,Xa, Z<:Topl- At/Z}. By Corollary C.1-4(2)
E, X<:C, Y< :X, E'{Y,XB, Z<:Top I- At/Z}, so by (Type Rec) E, X<:C, Y<:X, E'(Y,XB I-
J.1(Z)(Ai/Z)), and by (Sub Ref!) E, X<:C, Y<:X, E'{Y,XJ I- J.1(Z)(Ai/Z)) <: J.1(Z)(At/Z)).
In the latter case, we have E, X<:C, E'{X,XI, Z<:Top I- Ai/Z+j(X,Xi, so by induction
hypothesis E, X<:C, Y<:X, E'(Y,X), Z<:Top I- Ai/Z+}(Y,X& <: Ai/Z+IIX, YJ . By Corol-
lary C.1-4(2) E, X<:C, Y<:X, E'(Y,XB, Z<:Top I- Ai/Z+l«Y,Xl Since Ai/Z+l«Y,XB is
smaller than A, we use the induction hypothesis again with W;'dom(E, X<:C, Y< :X,
E'{Y,X&, Z<:Top), obtaining E, X<:C, Y< :X, E'{Y,Xl Z<:Top, W<:Z I- AIHWB(Y,X& <:
At(ZB(Y,XB. Therefore by weakening and (Sub Trans), E, X<:c, Y< :X, E' {Y,XB,
Z<:Top, W<:Z I- At(WJ(Y,X) <: At(ZHX,Y!, and by (Sub Rec) E, X<:C, Y< :X,
E'(Y,XB I- J.1(W)(A t IWJ(Y,XD) <: J.1(Z)(At{ZB(X, YD). That is, E, X<:C, Y< :X, E'{Y,XD I-
(J.1(Z)Ai/Zj)(Y,XB <: (J.1(Z)A/Zj)(X, YD·
Case A(V+,lll == 'V(Z<:A 1(tr,V-J)A2(V+,lll
We have E, X<:C, E'(X,XB I- At(X,XB and E, X< :C, E'{X,X), Z<:At(X,XB I- A 2(X,Xl
To apply the induction hypothesis we need to consider A'J ! At(V,Ul so
A ' i/V'",U-} and A'J(D',D" B == At(D',D"D for any D' and D". We have E, X<:c,
E'IX,X} I- A't{X,XB and E, X<:C, E'(X,Xl Z<:A't(X,XB I- A2(X,XB. By induction
C PROOFS 351

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

C.2 Proof of the Variance Lemma from Section 16.4


We extend the results of Appendix C1 to the object types with Self of Chapter 16.
Lemma C.2-1 (Variance)
Assume E'{V+,U-} and A{V+,U-} with y,.dom(E, X<:C, E') and XaV(E')uFV(A).
If E, X<:C, E'(X,XB I-- A(X,XB, then E, X<:C, Y<:X, E'(Y,XH I-- ACY,XB <: A(X, YB.
352 ApPENDIX. RULES AND PROOFS

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

C.3 Deriving the Rules for ~-Objects from Section 15.1.2


We verify some of the derived rules for C;-objects in detail. We omit the easier treatment
of other rules; in particular (Sub C;0bject) follows from (Sub Self).
(Val C;Select) (where A == C;(X)[li:B;{X+} iE1..n))
The derivation relies on the covariance of Bi in X, and subsumption.
E, Y<:A f-- Bjt¥} from the assumption of (Val C;Select)
C. PROOFS 353

E, Y<:A I- Bj[YH <: Bj{AH by Lemma 13.3-1


E, Y<:A, y:A(Y) I- Bj«¥» <: BjMH by weakening
E, Y<:A, y:A(Y) I- y.lj: Bjl¥» by (Val Select)
E, Y<:A, y:A(Y) I- y.lj: Bj«AH by (Val Subsumption)
E I- (use a as Y<:A, y:A(Y) in y.lj:Bj[A}}: BjiA} by (Val Use) since E I- a: A
E I- aA.lj : Bj{A} by definition
(Val C;Update) (where A ;; C;(X)[li:B;{X+} iE1..n])
The derivation relies on the full power of (Val Wrap). Note that y : A(y) does not
imply y : A(A), even though Y <: A. Otherwise b would only need to have type
BiM}, and this would permit unsound updates.
E, Y<:A, y:A(y), x:A(Y) f- b{Y,y,x} : Bj(YH by assumption
E, Y<:A, y:A(Y) f- y.lj~~(x:A(Y)b{Y,y,x} : A(y) by (Val Update)
E, Y<:A, y:A(Y) f- wrap(Z<:A=Y) y . lj~~(x:A(Z»b{Z,y,x» : A by (Val Wrap)
E I- (use a as Y<:A, y:A(Y) in wrap(Z<:A= Y) y.lj~~(x:A(Z»b«Z,y,x» :A) : A
by (Val Use) since E f- a: A
E f- a.lj~(Y<:A,y:A(Y)~(x :A(Y)b: A by definition
(Eq C;Object) (where A ;; C;(X)[li:B;{X+} iE1..n), A';; C;(X)[li:B;{X+} iE1..n+m))
E f- A' <: A by (Sub Self) and (Sub Object)
E f- bKH H b'{Cn : A'(C) by assumption
E f- pack Y<:A=C with b{Y}:A(Y)
H pack Y<:A'=C with b'{Y}:A'(Y) : 3(X<:A)A(X)
by (Eq Pack<:) since E f- C <: A'
E I- Jold(A,pack Y<:A=C with b{Y}:A(Y)
H Jold(A,pack Y<:A'=C with b'{Y}:A'(Y) : A by (Eq Fold)

E, Y, X<:Yf- 3(Z<:X)A'(Z) <: 3(Z<:Y)A(Z) as for (Sub Self)


E f- b'KH H b'KH : A'(C) by (Eq Symm) and (Eq Trans)
E f- pack Y<:A'=C with b'{Y}:A'(Y)
H pack Y<:A'=C with b'{Y}:A'(Y) : 3(X< :A')A'(X)
by (Eq Pack<:) since E f- C <: A'
E f- Jold(A,pack Y< :A'=C with b'{Y}:A'(Y)
H Jold(A',pack Y<:A'=C with b'{Y}:A'(Y): A by (Eq Fold<: Lemma1)

E f- Jold(A,pack Y<:A=C with b{Y}:A(Y)


H Jold(A',pack Y<:A'=C with b'{Y}:A'(Y): A by (Eq Trans)
E f- wrap(Y<:A=C)b{Y} H wrap(Y<:A'=C)b'{Y} : A by definition
(Eq Sub C;Object) (where A;; C;(X)[li:Bi{X+} iE1..n), A' ;; C;(X)[li:B;{X+} iE1..n+m])
E f- [li=~(xi:A(C»bi«q iE1..n) H [li=~(xi:A'(C»bi«q iEl..n+m) : A(C)
by (Eq Sub Object)
E, xi:A'(C) f- Mq : BiKq ViE1..n+m
354 ApPENDIX. RULES AND PROOFS

by a bound weakening lemma, using A'(C) <: A(C)


E I- [Ii=~(xi:A '(C)}Mq i<l..n+m] : A'(C) by (Val Object)
E I- wrap(Y<:A=C)[li=~(xi:A(Y)bily) iEl..n]
H wrap(Y<:A'=C)[Ii=~(xi:A '(Y)b;(Y} iEl..n+m]: A by (Eq Wrap Lemma)

(Eval <;Select) (where A == <;(X)[Ii:BiIX+) i<l..n], , == wrap(Z<:A=C)aIZ))


E, Y<:A, y:A(Y) I- y.I j : Bj(A} as for (Val <;Select)
E I- (use, as Y<:A, y:A(Y) in y.lj:Bj&A}) H a{q.lj : BjM) by (Eval Unwrap)
E I- 'A.Ij H a{q.lj : Bj(A} by definition
(Eval <;Update) (where A == <;(X)[Ii:Bi{X+) i<I...], ,== wrap(Z<:A=C)a{Z))
E, Y< :A, y:A(Y) I- wrap(Z<:A=Y) y.Ij~~(x:A(Z)}bIZ,y,x} : A as for (Val <;Update)
E I- (use c as Y<:A, y:A(Y) in wrap(Z<:A= Y) y . lj~~(x:A(Z)}bIZ,y,x}:A)
H wrap(Z<:A=C) alq.lj~~(x :A(Z)}b!Z,a{q,x} by (Eva I Unwrap)
H wrap(Z<:A=C) aIZ}.Ij~~(x:A(Z)}b{Z,aIZ},xj : A by (Eq Wrap)
E I- 'oIj~(Y<:A,y: A(Y)~(x :A(Y)bIY,y,x}
H wrap(Z<:A=C) aIZ}.lj~~(x : A(Z)}b(Z,aIZ},x} : A by definition

C.4 Denotational Soundness of Equational Rules


In this appendix we prove the soundness of the equational rules of FOb<:jI1 in the deno-
tationa! mode! of Chapter 14.
First we state some basic properties of records:
Proposition C.4-1
(1) Let InK = Ill. If (Xi,X;') E Ti for all i E I, then «(mi=xi i<1», «mi=x;' iE', mk=Yk' kEK») E
«mi:Ti i<'».
(2) If (x,x') E «mi:Ti iE'» and (y,y') E Tk for some k E I, then (x(mk~y),x'(mk~Y'» E
«mi:Ti i.,».
o
We have analogous properties for objects. The next group of results concerns the
equality of explicitly given objects and the semantic properties of method invocation
and update.
Proposition C.4-2
Let InK = Ill. If (Xj,x;,) E [[mi:Ti i.,))--+ Tj for all JEI, then «(mi=xi i.,», «mi=x;' i." mk=Yk'
k<K») E I!(S)«mi:S~Ti i<'», and hence «(mi=xi i<1», «mi=xi' i<', mk=Yk' k<K») E [[mi:Ti i <')].
Proof
Let T = [[mi:Ti iEl)] and let R = I!(S)«mi:S--+Ti i<'». Note that R ~ T. The hypothesis
and Proposition C.4-1 yield that «(mi=xi i<'», «mi=xi';<', mk=Yk' kEK») E «mi:T --+ Ti i<'».
»
Since R ~ T, --+ is antimonotonic, and « ... is monotonic, it follows that «(mi=xi i<'»,
C PROOFS 355

«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 Object) (where A == [/j:Bj i<l..n))


E, Xi:A I- bi H b;': Bi 'v'iE1..n
E I- [li=~(xi:A)bi iE!..n] H [li=~(xi:A)b;' iE!..n] : A

The first hypothesis yields ([A.(xi:A)b;Dw[A.(Xi:A)b;']p') E [A~Bi)q. (The argument is a


special case of the one for (Eq Fun).) According to the definition of the semantics,
this means that ([A.(xi:A)b;Dw[A.(Xi:A)b;'Bp·) E ((mi:[Bi]Jq iE1..n))~[Bi1, for all iELn. Prop-
osition C.4-2 yields ((li=[A.(xi:A)bi]p iE1..n»'((/i=[A.(Xi:A)bi'Dp' iE1..n))) E ((li:[Bi]Jq i<1..n)).
That is, ([[li=~(xi:A)bi iE1..n]Dwff[li=~(Xi:A)bi iE1..n]Ip') E ([li:Bi iEl .. n]]Jq.

(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

The first hypothesis yields ([A.(xi:A)bi]w[A.(Xi:A)bi)p') E [A~Bi]Jq. (The argument is a


special case of the one for (Eq Fun).) According to the definition of the semantics,
this means that ([A.(xi:A)bilJp,[A.(Xi:A)bi]p') E ((mi:[Bil, jE1.."J)~[Bi1, for all iE Ln. Propo-
sition C.4-2 yields (((/i=[A.(Xi:A)bi]p iE1.."»,«li=[A.(xi:A )b;Dp' iE1..n, Ij=[A.(xj:A')bj]p'
jEn+1..n+m») E ((li:[Bi]Jq iE1.. nn. That is, ([[li=~(xi:A)bi iE1.."lBp,[[li=~(xi:A')bi iE1..n, Ij=~(xrA')bj
jEn+!..n+m]Bp') E [[lj:Bi iE1.."]]Jq. (Note that the second hypothesis is not used.)

(Eq Select)
E I- a H a' : [li:Bi iE1.."] jE Ln
E I- a.lj H a'.Ij : Bj

The hypothesis yields ([aDp,[a'Dp') E [[/i:Bi iE1..n]]Jq, so ([a]p,[a']Jp') E ((Ii:[Bi]Jq iE1..n)]. By


Proposition C.4-3, [alJp = [a'Dp' =.l or la]p, [a']Jp· E (L~D) . If [alJp = Ha']p' =.l, then imme-
diately [a.lj]p = [a '.IjDp' = .l so ([a.lj]p,[a'.Ij lp.) E [Bj]Jq. Otherwise, [a]p, [a']Jp· E (L~D) . By
Proposition C.4-4, [a]p(l j ), [a']p.(lj) E (D~D) and ([a]p(lj)([a]p),[a'Dp·(lj)([a'Dp'» E [Bj1,.
Since [aDp, [a'Dp' E (L~D) and [a]p(l j ), [a']p.(lj) E (D~D), this last result means ([a.lj]p,
[a'.lj]p') E [Bj]Jq.

(Eq Update) (where A == [lj:Bj j<l..n))


El-aHa' : A E,x:Al-bHb':Bj jELn
E I- a.lj~~(x :A)b H a'.Ij*~(x:A)b' : A

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.

(Eq Fold) (where A == J.I.(X)B{X))


E I- b H b': BfAI
E I- fold(A,b) H fold(A,b') : A
According to the hypothesis, ([b]w[b']p' ) E [B(AH]M, that is, ([b]p,[b']p') E [B]M(X<-IAb])
by Lemma C4-6. Therefore (A(V )[b]p,A(V')[b']p') E Univ~[B]M(XHAb]), that is, ([fold(A,
b)]p,[fold(A,b')]p') E Univ~[B]M(X<-IAb]). Since Univ~[B]M(X<-IAb]) = [A]M, we obtain that
([fold(A,b)]p,[fold(A,b')]p' ) E [A]M.

(Eq Unfold) (where A == J.I.(X)B{Xj


El-aHa':A
E I- unfold(a) H unfold(a') : BfAH

According to the hypothesis, ([a]p,[a]p') E [A]M. Since [A]M = Univ~[B]M(X<-IAItJ) and


(.1.,.1.) E Univ, we obtain that ([a]p(.1.),[a[p'(.1.» E [B]M(X<-[Ab])' By Lemma C4-6, this
means that ([a]p(.1.),[a]p'(.1.» E [BgAH]M. Since [a]p(.1.) = [unfo1d(a)[p and [a ' [p(.1.) =
[unfold(a')Bp, we obtain that ([unfold(a)]p,[unfold(a ')[p) E [B«AH]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

[A(x :A)b(x)]p = A(u)[b(x)]p(x<-u) and hence [A(x:A)b(x)]p = A(u)[b]p(x<-u)([x[P(x<-U) and,


since x is assumed not to occur free in b, [A(x:A)b(x)[p = A(u)[b[p(u). The hypothesis
also gives ([b]p(v),[b'[p'(v'» E [B]M, hence ([A(x:A)b(x)[p(v),[b'[p'(v'» E [B]M. Finally,
we have ([A(x:A)b(x)]p,[b'[p') E [A]M~[B]M . That is, ([A(x:A)b(x)]w[b']p' ) E [A~Bh
360 APPENDIX. RULES AND PROOFS

(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

As in the case of second-order application, we use that [V(X<:A)B{X)~ I: [B(q~.


Moreover, [(A.(X<:A)b)(C)Dp = [A(X<:A)bBp and [b{qBp' = [A(X<:A)bBp" Hence, using
the hypothesis, we obtain ([(A(X<:A)b)(C)Bp,[b(qDp') E [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

This case is trivial, since [A(X<:A)b(X)Bp = [bBp.

(Eval Unpack<:) (where c;: pack X<:A=C with b{X):B{X})


E I- c :3(X<:A)B{X) E I- D E , X<:A, x:B{X) I- d{X,x) : D
E I- open cas X<:A,x:B{X) in d{X,x):D H d(C,b{q& : D

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

E (L-tD) and [A(xrA')bjllp' E (D-tD). Hence [a.ljllp' = [A(Xj:A')bjllp,([allp')' so [a.ljllp' =


[bjllp'(x;<--ialp')' Lemma C.4-6 yields [a.ljllp' = [bj(aHllp" hence ([a.ljDp,[bj{a}Dp') E [Bjlh,.

(Eva! Update) (where A == [li:Bi i<I .. n), a == [1;=<;(xi:A')bi i<1..n+m))


E I- a : A E, x:A I- b : Bj jE l..n
E I- a.lj~~(x:A)b H [lF~(x:A')b, li=~(xi:A')bi ie(1..n+m}-VI] : A

The first hypothesis yields ([allp,[allp') E (AIh, . That is, «(li=(A.(xi:A)b;Dp}),


((l;=[A(Xi:A)biDp')}) E [(li:[Bilh,ll. The argument for (Eq Update) gives ([a.lj~~(x:A)bDp,
(a.lj~~(x:A)bDp') E [AIh,. In addition, [alp' E (L-tD). Hence [a.lj~~(x:A)b]p' =
[[lj=~(x:A')b, li=~(xi:A')bi ie(1..n+m)-VI]]p' and ([a .lj~~(x:A)bDp,[[lj=~(x:A')b, li=~(xi:A')bi
ie(1..n+m}-VI]D p') E [AIh,.

(Eval Fold) (where A == I1(X)B{X})


El-a:A
E I- fold(A,unfold(a» H a:A

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,.

(Eval Unfold) (where A == I1(X)BfX))


E I- b: B«A}
E I- unfold(Jold(A,b» H b: BiAH
Since [unfold(Jold(A,bmp = [bDp, we obtain that ([unfold(Jold(A,bmp,[bDp') E [BtARIh,
directly from the hypothesis that ([bllp,[bDp') E [B«Ailh,.
o
List of Figures
2-1. Naive storage model. . . ... . . . . .. . . . . . .. ..... .. .. ..... . . .. . .. ... . .. .. . . 12
2-2. Method suites. . .. .. ... .. .... . . ... . .. . . ... . . ... . . . . .. .. . .... ... . .. . . . . 13
2-3. Hierarchical method suites. ... ... ..... ... . . .. .... .. . . . .. ..... .. . . ... .. 16
2-4. Collapsed method suites. . . .. .. .. . .. ... . .......... ... .. . .. . ..... .. . .. 17
4-1. Embedding. . . .... . . . ....... ... .. . . . ... . .. .. ... .. .. .. . . .... .. ... . ... . 39
4-2. Delegation. . . . ...... . .. . ... . .. . . ... ..... ....... ... .... .. . . ...... .. .. 42
4-3. Reparenting. . ..... . . . . .. . .. . .... .. . . ... .... ... .. . ... .. . .. . . .. .. .... . 46
4-4. Traits. . .. . . ... .......... . ...... . . . .. . ...... . .. .. . . .. . . . ...... . ... .. . 48
20-1. Binary trees . . . ... . .. . . .... .. . ... ... . . ....... . . ... . .. .. . . . .. . . .. .. .. 294
List of Tables
2 Class-Based Languages
Argument for the covariance ofAxB . . ... .. ... .. . • . ... . .. . . . ..... . . . . . .. 20
Argument for the col contravariance of A-tB . . . . . . . . . . . . .. . . . . . . . ... . .. . 21
Argument for the invariance of A>lf<B . . . .... . . . ... . .. . ......... . .. . .. . .. . 21

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

10 Untyped Imperative Calculi


Syntax of the imp~-calculus ............ ..... . ......................... 129
Syntax of the imp~f'calculus . ....... .... ...... .. . . ...... .............. 130
Translation of imp~f into imp~ ..... .... . . .. .. . . . .. . .. .. . ... ... . . . ..... 131
Syntax of the impA-calculus . . ..... . .. . ..... . ............ ..... ......... 131
Translation of the impA-calculus into the imp~f'calculus . . .. ..... ... .. .. .. 132
Syntax of the impA~f'calculus . . .......... . ... . . . . . . . . . . .............. . 133
Operational semantics ........ . .... .. ... ... .. . .. . . ..... .. ... ......... 136

11 First-Order Imperative Calculi


Typing rules ..... . . . . . ........ . ....... .. . .. . .. . . . .... ............... 141
LISf OF TABLES 367

Store typing .. . ........ .. . .. . . . .... . .. ..... .. . .. .. . . .. .. .. . ...... . .. 147

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

15 Definable Covariant Self Types


C;-Object operations .. . . ... . .. .. ........... . ..... . ... . . . .. . . . . ... . .... 202
l!.~+ . ... ..... . . . ........ . . . . .. . ... . . . .. .. .. .. . . .. . . .......... ... . ... 203
l!.=~+ . .. . .. . .. . .... . .. . . ............ .... . . .... . .. .. ........... . ... . . 203
Objects, with preliminary structural assumptions ... . .. . . . . ... ... .. . . .. . . 217
Objects, with structural assumptions .. . ......... . ... .. ..... . ... .. . . . .. . 218

16 Primitive Covariant Self Types


Syntax of S types . .... . .. . . .. . . . . ... .. . . . .... . .. .. .. .. .. . ..... .. . .. . . 223
Variant occurrences .... . ... ....... . .... ..... .... .... . ... . ..... . .. ... . 223
Syntax of S terms ......... .. .. . .. . ... ..... .. . .... .. .. ......... .. .. . .. 224
Operational semantics . ... ... . ........ . ... . .... ... . . .. .. .... ..... . . . . 224
368 A THEORY OF OSJECfS

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

17 Imperative Calculi with Self Types


Syntax of the imp~-calculus .... . . ......... .. ... ..... .... .............. 241
Operational semantics of method update .......... . ... ............ . .... 242
Terms .......... .. ...... ..... ....................................... 243
Syntax of the imp~v-calculus .............. ... .... . ......... .. ......... 244
Operational semantics for polymorphism .. .... .. .. ...... ........ ... . . .. 244
Quantifier rules . .. ... . . ..................................... ... .. . . . 244
Substitution of type stacks .... . ... ... . . ... . ................. . . . . .... .. 247
Store typing .......................... . ................... . .. . ... . .. 248

18 Interpretations of Object Calculi


Syntax for separate fields and methods ..... ..... ................. . ..... 258
Syntax with Self types ... ....... . .. .... .... ...... . . ....... . ...... .. ... 259
Untyped self-application interpretation .................... . ..... . ... . . 259
Untyped imperative self-application interpretation .. ..... ... . . . ... ...... 260
Untyped recursive-record interpretation without update ............ .... . 260
Untyped recursive-record interpretation with internal field update . .. .... . 260
Untyped state-application interpretation ......... ... ..... .... . .. ... ... . 261
Untyped state-application interpretation (continued) . . . . ................. 261
Untyped cyclic-record interpretation . .. .... ............ .. . .. . .. .... . . . . 262
Untyped split-method interpretation ............ .. ..... .. .. . ........... 263
Self-application interpretation ..... . ..... . .. .. .... . ..... . .. . . . ......... 264
Recursive-record interpretation, without update .. .......... .. ... . ....... 265
Recursive-record interpretation with internal field update . ... . . .... .. . ... 265
State-application interpretation .. ..... . .... . . .......................... 266
Cyclic-record interpretation ...... .. . .... ............. . .. ...... .. .... .. 267
Split-method interpretation (initial version) . . ... ...... ... ............. . . 268
Split-method interpretation . ... ............ ... ... ........ . ... . . ... . . .. 269
Split-method interpretation (with Self types) .............. .. . ... .. .. . ... 270
Imperative self-application interpretation ... . . .. .... ............. . . ... . . 271

19 A Second-Order Language
Syntax of 0-2 types . ...... . . ................... .. ... . .. . . ............ 274
Syntax of 0-2 terms .. ............ . .. ... ... ... . ...... .... ...... .. .... 275
Judgments ............ .... ...... .. ...... . . . ..... . .. . ............. .. . 279
LIST OF TABLES 369

Environments . ....... . . . .. . ... . . . .. . . . .... .. .. . ......... . . . . . ..... . . 279


Types . .... . . . . .. . .... . .. . . . ... .. . . . .. ... .. . . . . . . . ..... . ... . . . .. . . . . 279
Subtyping ..... .. . . . . . ... ... . . .. .. .. ....... .. . ... .. .... .. .... . ... . .. 279
Terms . .. . . . . .. .. . ... .... .. . . .. ... . . . . . ... .. .. ... . .. . .. . .. ..... .. ... 280
Translation of 0-2 types . . . . . .. . . . . ..... ... .. . ....... .... . .. . .. . ... .. 282
Translation of 0-2 environments . . ..... . .. . . . .. . .... . ......... . ....... 282
Translation of 0-2 terms ... ... .. . ..... . . . .. .. . .... .... .. .. . ... . . . ... . 283

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

21 A Language with Matching


Syntax of 0-3 types .. . ......................... .. ..... . ..... . ..... . .. 306
Syntax of 0-3 terms . ... . ... . . . .. . ..... . .. . . . . . . . . ..... . . . . ... .. ... . . 306
Judgments . . . .. ..... . ..... .. ... . .... ... .... . ... . ... . ....... . .. . ..... 311
Environments . . . .. . . .. . . ... . . . ... . . . . . . . . . . . .. .. .. . . . . .. . . .. . . . .. . . . 311
Types ............ . .. . . . .... .. . ...... . .... . . .... .. ...... . ......... . . 312
Object types ... .. . . .. . .. . . . .. . . ... . .... . .. . . .. ........ . ... . .. . ...... 312
Subtyping .. . .. . ......... . .. ...... . . . . . . . .. ... . . .. .. .. . .. . . . .... .. . . 312
Matching . . ... ... . ... . . . .. .... .. . . . .. . . .. . ... . ... . .. . . . . . . .. . .. . . ... 313
Terms .. . . .. . . .. . ......... .. . . . . ..... . ..... . .... ... . .. . . . . . ... . . .. .. 313
Translation summary . . .... .. . . . .... . . . ........ . ... . .. . .... .. . . . . . . .. 316
Translations for 0-3 .. . . . .. . .. . . . ........ ... .. . . . .. ... ......... . . . . . . 317
Translation of 0-3 types .. . ..... . .. ... ... . .. . ..... . . . . . . ..... .. . ..... 318
Translation of 0-3 environments .. . ... ... ... ... .. ... . .. . .... . . . .. .. ... 318
Translation of 0-3 terms .... ... ........ . . . .. . .. . ... .. . .. . .. .. . . . .. .. . 318
Translation of 0-3 judgments .. . . . ... .. . . . . .. . . . ..... . .. . ... .. . . ... . .. 319
370 A THEORY OF OBJECfS

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

3 Advanced Class-Based Features


ObjectType object type . . . . . . . . . . . . . . . . . . . . . . .26. . . . . . . . .
ObjectTypeOJ type of an object . . . . . . . . . . . . . . . . . . . 26 . . . .. .. . .
ObjectOperator object type operator .... .. .. ....... . ...... . .. 29
type type . . .. ... . . ........ . .. . . .. ..... ... . ... ... 29
-<: suboperator relation . . .. . . . . . . .... .. . .... . .. . 32

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

copied from shorthand for embed . .. .. .. ................ . 40


extends embedding relation between objects .. ..... . ... 41
override used to replace an attribute in a derived object .. 41
child of delegation relation between objects . . .. .. . .. . .. 43
delegate indirection to a method of another object ....... 44
reparent dynamic change in the child of relation ....... . 47

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

(Rule name) (Annotations)


E1 f- 51 ... En f- 5 n rule . . . .. . .. ... . . . . .. . . .. . .... .. . .. . . . .... . 79
Ef-5

r .~~ Tn
derivation tree ............. . ... .... . ...... .. 79

Judg ment

Ef-o environment judgment ........ . . . . ......... . 79


A, B, C types .... . ............ . ..... . .. . ... .. . . .... 80
[lj:Bj jE1..n] object type . . . . . . . . . . . . . . . . . . . . . . .80. . . . . . . . . .
Ef-A type judgment . . ... . ... . . . . ... . . .. . . . .... . .. 81
Ef-a:A typing judgment . . . . . . . .. . ... . ....... . .. .. . . 81
[lj=~(xj:Aj)bj jE1..n] object (with typing) . . . . . . . . .... .. . . .. . ... . .. 81
a.l~~(x:A)b method update (with typing) ... . ..... . .. . .... 81
[ .. ., l,m :B, ... J [ ... ,I:B, m:B, ... J . ..... . .. . .. .. . .. . . .... . . . .. . 82
dom(E) set of variables defined in environment E . . . ... 82
ObI the first-order typed ~-calculus ............ . .. 83
Fl the first-order typed A-calculus . .. .. .. ....... . 83
FObl the first-order typed A~-calculus .... . .. . . . . . . . 83
Baal boolean type . . . . . . . . . . . . . . . . . . . . . 83 .. . . . . . . . .
Nat natural number type . ...... . .... .. ... .. . . . .. 83
Int integer number type ... . ..... . . . . . . .. . . . . . .. . 83
Real real number type . . .. .. . . .. . .. .. . . .. .. . .. . .. 83
x :A~a x ~ a and E f- a : A for appropriate E . . . ........ 84
Ef-bHC : A equivalence judgment . . . . . .. ... .. .. . . .. . . .. . 89
fiXA fixpoint operator at type A . .. . . . . ... . .. . . .. . . 92
l1(x:A)b recursive term (with typing) .... ..... .. . . . . . . . 92

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

Class(A)lns,Sub class type with visibility restrictions . . . .... .. . 102


(li:Bi if1..n) record type ............ . . .. ... ... .. . . ..... 106
a.li;,(y:Bi)C;(x:A)b method update with elder . . . .. . .. . . ... ..... 108
lltUi:Bi if1..n] object type with variance annotations Ui . . .. . .. 110
+
covariant (read-only) variance annotation . . . .. 110
contravariant (write-only) variance annotation. 110
o
invariant (read-write) variance annotation ... . 110
E I- u B <: u' B' subtyping judgment with variance annotation . 110

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

10 Untyped Imperative Calculi


impc; the imperative untyped c;-calculus ... .. ..... . . 129
clone(a) cloning (shallow copy) .. . . . . . . . .... ... . . ... . 129
let x=a in b local definition of an immutable identifier .. . . . 129
a;b sequentialevaluation .. . .... . . ....... . .. .. .. 130
imp9 the imperative c;-calculus with fields . ... . . .. . . 130
impA the imperative A-calculus . . . .. . .... . . . . ..... 131
impA9 the imperative Ac;-calculus with fields . ... ... . 132
j1l empty translation environment . . . . . . ... ... . . 132
p{yt-al translation environment extension . .. .. ... .. . 132
LIS[ OF NOTATIONS 375

var x=a in b local definition of a mutable identifier .... .. .. 132


x:=a assignment to a mutable identifier ...... ... . . 131
let x=a; top-level definition .... . .. .. . .. . . ... . .... .. . 133
l store location. . . . . . . . . . . . . . . . . . . . . 136 . .. . . . . . .
v ::= (li=li i~ 1..nl object result . . ... . . .. . .. ...... . ... .... .. .. . 136
a ::= li.... (~(Xi)bi,Si) iE 1..n store ....... ... ... ...... ........ . ...... . .. 136
store update .... . ... . .. ... .. . .. . ........... 136
S ::= Xi ....Vi iE1..n stack .......... . .... . ...... . . ... . .. . .... . . 136
01-<> well-formed store judgment . .. ... .. .. .... . .. 136
a.S I- <> well-formed stack judgment .. . .. .. ... . ... ... 136
a. S I- a -- v.a' term reduction judgment . . . ...... . ....... .. 136
dom(a) set of location defined in a store .............. 136

11 First-Order Imperative Calculi


M := [li:Bi iE1..nl~Bj method type ... ...... .. . .. . ...... .. . ... ... 147
l: ::= li....Mi iEl .. n store type .................. . .............. 147
l:(l) method type for location l. . .... . ... . ....... . 147
l:l(l) self type of method type at l ........ .. .... . .. 147
~(l) result type of method type at l. . . .. . . . . . . . . . . 147
1= ME Meth well-formed method type judgment. . .... .... 147
l:1=<> well-formed store type judgment ........ . . .. 147
l:l=v:A result typing judgment . . .. . .... . . . .... . . . .. 147
l:I=S:E stack typing judgment .... .. .. . ... . . .. .. .... 147
l:l=a store typing judgment .. .................. . . 147
l:' ~ l: l:' is an extension of l: . . .. . .. .... .. . .... .. .. 148

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

wrap(Y<:A=C)b element of a Self-quantified type .. . ... . . . . . .. 181


use c as Y<:A, y:B in d:D using an element of a Self-quantified type . .... 181
wrap(A,c) wrap(X<:A=A)c, for a fresh X .. . . .. ... .. ..... 182
wrap(X=A)c wrap(X<:A=A)c . .. . . . . . . .. ... .. .. . . .... .. .. 182

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

semantic object types .... . . . ... ... .... . . .... 189


po restriction of the relation P to finite elements .. 191
T(P) transitive closure of P .... ... ....... . .... ... 191
U(P) completion of P ..... . ... . . .. . . ........ . . . .. 191
NUSR nonempty uniform symmetric binary relations 191
NUPER nonempty uniform pers ... .. .. .. . .. . .. ..... 191
TV set of type variables . . ......... . ... .. ... .. .. 196
TE set of type expressions . . . .. ... ..... . .. . .. ... 196
11 type environment . . . . ..... .. . . . .. . . .. . ..... 197
ITAIIrJ semantics of a type . . . . . . . . . . . . . . . . . 197
. ... . ...
p value environment . . . ...... .... . ..... .. .. .. 197
ITaBp semantics of a term ... .. ... .. ... . ... . ... . ... 197

15 Definable Covariant Self Types


~(X)[lj:Bj(X+) j£l..n] ~-object type ... . . . .... ... ..... .... ..... ... 201
aA.lj ~-object selection .. .. .. .. . . . ... .. . .. . . ... .. 202
a.lj~(Y<:A,y:A(Y)~(x:A(Y)b ~-object update . . . ... . .. . . .. . . . . ... ........ 202
Class(A) class type(for ~-objects) . . . . . ........ ... . . . . 210

16 Primitive Covariant Self Types


Obj(X)[lj:Bj j£l .. n] structural object type . .. . . .......... ... . .. . . 216
obj(X=C)[lj=~(xj : X)bi j£l..n) structural object ... . ..... . ... .. ..... . . .. . . . 216
a.l structural method invocation . . .. . ... . ... . . . . 216
a.l~(Y< :A,y: Y)~(x: Y)b structural method update ........ .. . .. ... .. . 216
C(D) unfolding of structural object type .. . ........ 216
a(D) unfolding of structural object .. ... ..... .... . . 216
S calculus with Self types and structural rules . .. 222
Obj(X)[ljUj:Bj{X+) j£1..n] structural object type with variance .. .. .. . . .. 223
B(X+) positive occurrence .. .. . . . . . ... . ... .. ... ... 223
B{X-) negative occurrence ...... . .............. . .. 223
Sit S with universally quantified types . ... . . . ... 228
Class (A) class type (for primitive Self types) . ....... . .. 237
Class(A)'ns,Sub class type with visibility restrictions ... . . . . . . . 239

17 Imperative Calculi with Self Types


a. l~(y,z=c )~(x)b method update (generalized) . . .. . .. . . . . .... . 241
AOb dummy type abstraction .. .. . ... . ... .. . . . . .. 243
aO dummy type application .. . .. . . ...... .. .. . . . 243
imp~1t the imperative ~-calculus with AOb and aO . . . . 244
(AOb,S) type abstraction result ... . .. ...... ... .. . .... 244
[lj :Bj j£l..n] object type . . . . . . . . . . . . . . . . . . . . . .245. . . . .. . . . .
a.l~~(x)b method update . . .... . . .. . . . .... .. ... . .... . 245
let x=a in b local definition ..... . . . .. ..... .... . .... .. . . 245
LIST OF NOTATIONS 379

[li=b i iEl..n, Ij=~(xj)bj jEn+l..n+m] object with fields .. . . . ......... . .. . .. .. . .. . . 245


a.l:=b field update . .. . . . . . .. .. . ........ . . . .. . ... . 245
a.l~(y)~(x )b method update .. ... . .. . ... .. ..... . .. . . .. .. 245
T ::= Xi~Ai iEl..n type stack . . .. ........... . ... . .. .... . .. .. .. 247
/+-T} type stack substitution . .... . ........ .. ...... 247
M ::= Obj(X)[liVi:Bi iEl..n]~j method type . . . . . . . . . . . . . . . . . . . . .248 . .. . . .. . .
r ::= ti~Mi iEl..n store type . . . . . . . . . . . . . . . . . . . . . . .248 .. .... . . ..
rl(t) self type of method type at t ....... . . . ... .... 248
:E:!(A,t) result type of method type at t . . . . . . . . . . . . 248 . . .
I=M E Meth well-formed method type judgment . .. . ... . .. 248
rl=o well-formed store type judgment . ... . ... . .. . 248
rl=v : A result typing judgment . . . . . ........ . ....... 248
r 1= S.T: E stack typing judgment . ...... . . . . ... . ..... . . 248
rl=a store typing judgment ..... .. ...... .. . . . .. .. 248
dom(S) set of variables defined in stack S ...... . .. . .. 248

18 Interpretations of Object Calculi


[fk:Bk kEl..m I li:Bi iE1..n] object type with m fields and n methods ... . .. 258
[fk=bk kEl..m I li=~(xi:A)bi iEl .. n] object with m fields and n methods .. . .. . .. . . . 258
oAoJ field selection . . . . . . . . . . . . . . . . . . . .258 . .. . . . . . .
oA,f=b field update .. . . ........ .. . . . . ... . .. . ... . . . 258
oA.l method invocation . .. . .... . ..... . ... . ...... 258
Obj(X)[fk:Bk ke1..m I li:Bi/X+} iE1..n]
object type with Self, m fields and n methods . . 259
obj(X=A)[fFbk kEl..m I li=~(xi:X)bi iEl..n]
object with Self, m fields and n methods .. . .. . . 259
nil(C) un initialized value of type C .. . . .. . . . . . . . . . . 267

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

A ::K constructor A has kind K. . . . . . . . . . . . . .287


. . . ...
X<:A: : K X subconstructor of A at kind K . . . .. . . . .. .. .. 287
'It(X<:A ::K)B universally quantified type ....... ... . . . . .. .. 288
A(X<:A::K)b constructor abstraction . . . . . . . . . . . . . . 288 .. . ....
b(A) constructor application ... . .. . ..... . ... . .... 288
A(X::K)B operator . . . . . . . . . . . . . . . . . . . . . . . . .. 288. . . . . . . .
A(X)B A(X::Ty)B . ... . ... . .. . .. .. ......... . . . . . .. . 288
B(A) operator application . . .. .. ... ... . . .. . . . . . .. . 288
E f- K kind well-formed kind judgment . .. ..... . ... . .... 290
Ef- A :: K constructor kind judgment. . . . . . . . . . . . . 290 . . ...
Ef-A - B ::K constructor equivalence judgment. ...... .. ... 290
Ef- A <: B :: K constructor inclusion judgment ...... . . ...... 290
fKl maximum constructor of a kind . . . . . .. . ...... 290
X::K stands for X <: fKl:: K (in environments) .... . . 290
X<: A stands for X <: A :: Ty (in environments) . . ... . 290
X stands for X <: Top :: Ty (in environments) . ... . 290
Ef-A stands for E f- A:: Ty .. . ......... .. .. .. ... . . 290
Ef-A-B stands for E f- A - B :: Ty . .. .. . . . .. .. . . . .. . . 290
E f- A <: B stands for E f- A <: B :: Ty ... .. . . . . . . .. . ... . . 290
Op stands for Ty~ Ty .. . . . .... . . . .... .. .... .... 295
A -<: B stands for A <: B :: Op . ... ...... . .... . . ..... 295
A* fixpoint of the operator A . . . . . . . . . . . . . 295 .. . . . .
lubr(A) least upper bound of a constructor .... .. . . . . . 298
An! normal form of a constructor . . . . . . . . . . . 298 .... .
E f-n A <: B :: K normal subconstructor judgment .. . . . .. .. . . . 299
E f-n vA <: v' B normal subconstructor judgment with variance 299

21 A Language with Matching


object(x:A) li=bi iE!..n end object .. . ..... . .......... . ... . . ...... ..... 306
a.l := method(x:A) b end method update .. . . ..... . .. . . . . .... . .. . .. . . 306
subclass of c:C with(x:X<# A) subclass ..... . . .. ... ... ... .. . .. . . . ... . .. . . 306
li=bi iEn+!..n+m
override li=bi iEOvr\;!..n end
fun(X<#A) bend match-bound type abstraction . . . .. ..... . .. . . 306
E f- A:: Obj object type judgment ... . ... .. .. . . . ..... ... . 311
Ef-A<#B matching judgment . . ... . ..... . . . . .... . ... . 311
List of Languages
The following is a list of the languages mentioned in the text, with some bibliographic
references. There are many other object-oriented languages that we do not discuss
explicitly.

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

Conference on Object-Oriented Programming. Lecture Notes in Computer Sci-


ence 821, 236-259. Springer-Verlag.
[14] Albano, A., Bergamini, R., Ghelli, G, and Orsini, R. 1993. An object data model
with roles. In Proceedings of the 19th International Conference on Very Large
Data Bases, 39-51.
[15] Albano, A., Cardelli, L., and Orsini, R. 1985. Cali/eo: A strongly typed, interactive
conceptual language. ACM Transactions on Database Systems 10(2); 230-260.
[16] Albano, A., Ghelli, G, and Orsini, R. 1995. Fibonacci: Aprogramming language for
object databases. The Very Large Data Bases Journal 4(3); 403-444.
[17] Amadio, R. M. 1991. Recursion over realizability structures. Information and Com-
putation 91(1); 55-85.
[18] Amadio, R. M. and Cardelli, L. 1993. Subtyping recursive types. ACM Transac-
tions on Programming Languages and Systems 15(4); 575-631.
[19] America, P. 1989. A behavioural approach to subtyping in object-oriented program-
ming languages. Philips Research Journal 44(2-3); 365-383.
[20] America, P. and van der Linden, F. 1990. A parallel object-oriented language with
inheritance and subtyping. In Proceedings of the ACM Conference on Object-Ori-
ented Programming Systems, Languages, and Applications, and of the Euro-
pean Conference on Object-Oriented Programming, 161-168.
[21] Andersen, B. 1992. Ellie: A general, fine-grained, first-class, object-based language.
Journal of Object-Oriented Programming 5(2); 35-42.
[22] Apple 1993. The NewtonScript programming language. Apple Computer, Inc.
[23] Arnold, K. and Gosling, J. 1996. The Java™ programming language. Addison-Wes-
ley.
[24] Barendregt, H.P. 1985. The lambda calculus: Its syntax and semantics . North-HoI-
land.
[25] Birtwistie, G. M., Dahl, O.-J., Myhrhaug, B., and Nygaard, K. 1979. Simula Begin.
Studentlitteratur.
[26] Blaschek, G. 1991. Type-safe OOP with prototypes: The concepts of Omega. Struc-
tured Programming 12(12); 1-9.
[27] Blaschek, G. 1994. Object-oriented programming with prototypes. Springer-Verlag.
[28] Bohm, C. and Berarducci, A. 1985. Automatic synthesis of typed A-programs on term
algebras. Theoretical Computer Science 39(2-3); 135-154.
[29] Booch, G. 1986. Object-oriented development. IEEE Transactions on Software Engi-
neering 12(2); 211-221.
[30] Booch, G 1994. Object-oriented analysis and design with applications . Benjamin/
Cummings.
BIBLIOCRAPHY 385

[31] Borning, A H. 1981. The programming language aspects of ThingLab, a constraint-


oriented simulation laboratory. ACM Transactions on Programming Languages
and Systems 3(4); 353-387.
[32] Borning, A H. 1986. Classes versus prototypes in object-oriented languages. In Pro-
ceedings of the ACM/IEEE Fall Joint Computer Conference, 36-40.
[33] Bracha, G. and Cook, W. 1990. Mixin-based inheritance. In Proceedings of the
ACM Conference on Object-Oriented Programming Systems, Languages, and
Applications, and of the European Conference on Object-Oriented Program-
ming, 303-311.
[34] Browne, R. 1994. EiJfel: Frequently asked questions. <comp.lang.eiffel> news-
group.
[35] Bruce, K. B. 1994. A paradigmatic object-oriented programming language: Design,
static typing and semantics. Journal of Functional Programming 4(2); 127-206.
[36] Bruce, K. B., Cardelli, L., Castagna, G., The Hopkins Objects Group, Leavens, G.
T., and Pierce, B. 1995. On binary methods. Theory and Practice of Object Systems
1(3); 217-238.
[37] Bruce, K. B., Schuett, A, and van Gent, R. 1995. PolyTOIL: A type-safe polymorphic
object-oriented language. Williams College Technical Report.
[38] Canning, P., Cook, W., Hill, W., Olthoff, W., and Mitchell, J. C. 1989. F-bounded
polymorphism for object-oriented programming. In Proceedings of ACM Conference
on Functional Programming and Computer Architecture, 273-280.
[39] Cardelli, L. 1988. A semantics of multiple inheritance. Information and Computa-
tion 76(2-3); 138-164.
[40] Cardelli, L. 1994. Extensible records in a pure calculus of subtyping. In Theoretical
Aspects of Object-Oriented Programming, C. A Gunter and J. C. Mitchell, eds., 373-
425. MIT Press.
[41] Cardelli, L. 1995. A language with distributed scope. Computing Systems 8(1); 27-
59.
[42] Cardelli, L. and Longo, G. 1991. A semantic basis for Quest. Journal of Functional
Programming 1(4); 417-458.
[43] Cardelli, L. and Mitchell, J. C. 1991. Operations on records. Mathematical Struc-
tures in Computer Science 1(1); 3-48.
[44] Cardelli, L., Mitchell, J. c., Martini, S., and Scedrov, A. 1994. An extension of sys-
tem F with subtyping. Information and Computation 109(1-2); 4-56.
[45] Cardelli, L. and Wegner, P. 1985. On understanding types, data abstraction and
polymorphism. Computing Surveys 17(4); 471-522.
[46] Cardone, F. 1989. Relational semantics for recursive types and bounded quantification.
In Proceedings of Automata, Languages and Programming. Lecture Notes in
Computer Science 372,164-178. Springer-Verlag.
386 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

- of record types 106, 112, 179 first-order - 89


- requirement for Self types 202, 223 untyped - 62
cuper (complete uniform per) 187 error value 185
cyclic-record interpretation 262, 267 existential
- quantifier 173
D bounded - quantifier 173
default parameters 67
delegation 13, 39, 42 F
dynamic - 46 F-bounded parameterization 33
static - 46 field 12,58
delegation-based 42 - selection 52, 58, 130
denotational semantics 185 - update 52, 58, 130, 245
derivation 63, 79 imperative -s 130, 245
disjoint union type 122 finiteness 187,188
dispatch fixpoint
dynamic - 18 - of a function on cupers 188
multiple - 53 - of an operator 295
single - 53 - operator 68, 92
static - 18 formal system 79
distance (for cupers) 188 fragment 79
dynamic equational - 84
- dispatch 18 free variable 61,118,176
- inheritance 46 fresh method 239
- typing 126 function 66
see also typecase - encoding 66,91,112,115,131,178
function type 21, 83
E - encoding 91,112,115,178
elder 108 contravariance of -s 21, 94, 112, 178
embedding 13, 39 subtyping of -s 21, 94
embedding-based 40
embedding-retraction pair 186 G
environment (for judgments) 79 grammar notation 60
environment (semantic) 197 ground type 82
type - 197
equational theory 84
H
higher-order
- and self 90
- for constructors 290 - bounded parameterization 33
- for existential quantifiers 174 - subtyping 295
- for records 106
see also subconstructor
host object 9, 38, 57
- for recursive types 114
- for Self quantifiers 181 I
- for structural objects 217 implementation
- for sub typing 98 - of abstract type 173
- for universal quantifiers 171 - of inheritance 14
- for <;-objects 203
INDEX 393

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

encoding of - inheritance 75, 101 p


parametric in Self 125, 211, 237, 274, 281
N
parent link 42
narrow 20
partial equivalence relation 187
negative occurrence 177, 223
partially abstract type 29, 173
new 12
per (partial equivalence relation) 187
- in 0-1/2/3 155,275,307
polymorphism 169, 274
see also class
nonexpansiveness 188,194,197 bounded - 171
subtype - 17
o positive occurrence 177,223
object 7,57 pre-method 23,73,100,144,209,214,237,
- extension 38, 60 297
- in 0-1/2/3 153,273,307 premise judgment 62, 79
- protocol 25 primitive semantics 58
- shape invariant 215 private method 102
- without class 35 product type 20
--orientation 7 - encoding 111
donor - 38 covariance of -s 20,111,179
host - 9, 38, 57 projection 186
prototype - 47 protected method 102
structural - 216 protocol 32
trait - 47 prototype 36, 47
C;-- 201 public method 102
object type 26, 35, 80, 189, 288
- in 0-1 /2/3 153,273,305 Q
- with Self 201, 222 qua 16,20
covariance of -s 109 quantifier
invariance of -s 94 bounded existential- 173
structural - 216 bounded universal - 171, 228, 243,
subtyping of -s 94 288
C;-- 201 bounded universal - in 0-2/3 274,
object-based 1, 35, 155, 273, 305 306
occurrence existential - 173
negative - 177,223 Self - 169,179,201,269
positive - 177,223 universal- 170
operational semantics 64, 86, 120, 127,
135,224,228,242,244,289 R
operator rank 187, 188
- abstraction 288 record 76,106, 259
- application 288 - encoding 112
fixpoint of an - 295 record type 106, 179, 264
type - 287 - encoding 112,179
override 9, 74 covariance of -s 106, 112, 179
-inO-1/2/3153,273,307 subtyping of -s 106
INDEX 395

recoup 212 see cloning


- invariant 214 single
recursive type 113 - dispatch 53
invariance of -s 116 - inheritance 16
recursive-record interpretation 78, 260, - inheritance in 0-1/2/3 153, 273,
265 305
reduction 61 specialization
weak - 64 method - 22, 28, 239, 273, 282
reparent 47 Self type - 24
representation type 173,202 split-method interpretation 263, 268
resend 16 stack 136, 242
result 64, 86, 120, 136, 242, 244, 289 - type 247
- typing 146, 248 - typing 147,248
root class 155, 276, 307 state-application interpretation 261,266
rule 79 static dispatch 18
- naming 79 store 136, 242
congruence - 62 - typing 146, 248
structural - 172, 190, 216, 221, 243, structural
293 - invariants 216
- object 216
S - object type 216
scoping 61, 118, 176 - rule 172,190,216,221,243,293
selection 52 - subtyping 172,190
selection (of field) 52,58, 130 - subtyping invariant 215
Self - update 172,190,222,243,293
- quantifier 169, 179, 201, 269 stuck 87, 138, 152
- type 24, 30, 125, 169, 201, 221, 242, subclass 15, 27, 30
294 -in0-1/2/3155,273,307
- type in 0-2/3 273,305 - interface 102, 239
- type specialization 24 see also class, inheritance
see also method specialization subconstructor 287, 290
parametric in - 125, 211, 237, 274, see also higher-order sub typing
281 subject reduction 86, 97, 121, 127, 146,
self 9,57 229, 247, 301
self-application suboperator 295
- interpretation 76, 185, 259, 264, 271 subprotocol 32
- semantics 185 substitution 59,61,113,120
self-unfolding 202 subsumption 18, 93
semantics - in 0-1/2/3 154,273,305
denotational - 185 subtype polymorphism 17
operational - 64, 86, 120, 127, 135, subtyping 18,27,30, 93
224,228,242,244,289 - by type name 27
self-application - 185 - by type structure 27
shallow copy - for existential quantifiers 174
396 A THEORY OF OBJECTS

- for function types 21, 94 object - in 0-1/2/3 153,273,305


- for object types 94 object - with Self 201, 222
- for object types with primitive Self partially abstract - 29,173
and variance 226 product - 20
- for object types with Self 204 record- 106,179,264
- for record types 106 recursive - 113
- for Self quantifiers 180 representation - 173,202
- for universal quantifiers 171 Self - 24, 30, 125, 169, 201, 221, 242,
- for variance annotations 110 294
- in 0-1/2 153,273 Self - in 0-2/3 273,305
higher-order - 295 stack - 247
see also subconstructor store - 146,248
multiple - 27 unique -s 85
structural- 172, 190 <;-object - 201
sum, coalesced 186 typecase 19,23,126
super 15 - in 0-1/2/3 156,274,311
- encoding 74,101 typing environment 79
-in0-1/2/3156,276,307
syntax notation 60 U
unfolding 202
T union type, disjoint 122
trait 47, 144 unique types 85
- encoding 73,101 unit 122
see also class encoding unit type 122
true type 18 universal
type - quantifier 170
- abstraction 169, 243 bounded - quantifier 171,228,243,
- application 169, 243 288
- operator 287 bounded - quantifier in 0-2/3 274,
- parameter 28 306
- variable 113,171 update
abstract - 173 field - 52, 58, 130, 245
bounded - parameterization 29 method - 14, 37, 52, 58, 59, 129, 210,
bounded - variable 171 224,241
bounded abstract - method - in 0-1/2/3 153,275,307
see partially abstract -
disjoint union - 122 V
dynamic - 126 valid judgment 63, 80
see also typecase variance
environment (semantic) 197 see co-, contra-, in-
function - 21, 83 variance annotations 110, 124, 222
method - 146, 247 - in 0-1/2/3 154,274,307
minimum -s 95,118 virtual method 15
object - 26, 35, 80, 189, 288

You might also like