Disjoint Intersection Types: Theory and PracticebyXuan Bi(毕旋)

A thesis submitted in partial fulfillment of the requirements forthe degree of Doctor of Philosophy
at The University of Hong Kong

December 2018

Abstract of thesis entitled“Disjoint Intersection Types: Theory and Practice”
Submitted by
Xuan Bi
for the degree of Doctor of Philosophy
at The University of Hong Kongin December 2018

Programs are hard to write. It was so 50 years ago at the time of the so-called software crisis;
it still remains so nowadays. Over the years, we have learned—the hard way—that software
should be constructed in a modular way, i.e., as a network of smaller and loosely connected
modules. To facilitate writing modular code, researchers and software practitioners havedeveloped new methodologies; new programming paradigms; stronger type systems; as well
as better tooling support. Still, this is not enough to cope with today’s needs. Several reasons
have been raised for the lack of satisfactory solutions, but one that is constantly pointed out isthe inadequacy of existing programming languages for the construction of modular software.
This thesis investigates disjoint intersection types, a variant of intersection types. Disjoint
intersections types have great potential to serve as a foundation for powerful, flexible and yet
type-safe and easy to reason OO languages, suitable for writing modular software. On thetheoretical side, this thesis shows how to significantly increase the expressiveness of disjoint
intersection types by adding support for nested composition, along with parametric polymorphism. Nested composition extends inheritance to work on a whole family of classes, enabling high degrees of modularity and code reuse. The combination with parametric polymorphism further improves the state-of-art encodings of extensible designs. However, theextension with nested composition and parametric polymorphism is challenging, for two
different reasons. Firstly, the subtyping relation that supports these features is non-trivial.
Secondly, the syntactic method used to prove coherence for previous calculi with disjoint
intersection types is too inflexible. This thesis addresses the first problem by adapting andextending the well-known BCD subtyping with records, universal quantification and coercions. To address the second problem, this thesis proposes a powerful proof method toestablish coherence. Hence, this thesis puts disjoint intersection types on a solid footing by
thoroughly exploring their meta-theoretical properties.

On the pragmatic side, this thesis proposes a new language design with support for firstclass traits, dynamic inheritance and nested composition. First-class traits allow two objectsof statically unknown types to be composed without conflicts. Dynamic inheritance allows
a class to inherit from other classes at run time. To address the challenges of typing firstclass traits and detecting conflicts statically, this thesis shows how to model source language
constructs for first-class traits and dynamic inheritance by leveraging the fine-grained expressiveness of disjoint intersection types. To illustrate the applicability of the new design,
this thesis conducts a case study that modularizes programming language features using ahighly modular form of Visitors.
All the results and metatheory presented (unless otherwise indicated) in this thesis aremechanized in Coq in order to show the rigorousness of the approach. This thesis unifies
ideas that are seemingly unrelated but powerful on their own—dynamic inheritance, firstclass traits, nested composition—by a lightweight mechanism, thus providing new insights
into software modularity and extensibility.

An abstract of exactly 500 words

To my beloved parents

Declaration
I declare that this thesis represents my own work, except where due acknowledgment is made,and that it has not been previously included in a thesis, dissertation or report submitted tothis University or to any other institution for a degree, diploma or other qualifications.

.........................
Xuan Bi
December 2018iAcknowledgments
First and foremost, I would like to thank my advisor Dr. Bruno C. d. S. Oliveira for his continued support and mentorship throughout my studies. It is my honor to be one of his first
disciples. Back in 2014 when I was in my final year at ZJU, I wasn’t sure if I wanted to pursue
a PhD, let alone a PhD in Programming Languages! My fellow classmates were all considering applying for PhD in AI, computer vision, etc; there were no courses on the theories ofprogramming languages in the computer science curriculum in most (if not all) universities
in Mainland China. My first encounter with this field was due to a Coursera course I happened to enroll in. Immediately I got fascinated and wanted to dig more. Looking back, it
was such a great timing that Bruno just landed his faculty position as an assistant professor
at HKU, and that I just finished my undergraduate studies. Our initial conversation went
very well, and shortly I became his first PhD student. For the first few months, I was quite“on the right track”. I recall a conversation with my colleague (Haoyuan, is that you?) that
Bruno referred me as the “push model” because I always brought lots of questions in our
weekly meetings and pushed him to give me more tasks. It was not long before I lost myself,
had difficulty making any progress, and that the weekly meetings with Bruno soon becamenightmares to me. Bruno, thank you for not giving me up, encouraging me to keep reading
more papers, keep trying different ideas no matter how hopeless they may seem, and especially for not reminding me of the possibility that I may not be able to finish PhD on time!
All in all, I could not wish for a better PhD advisor.
I am incredibly fortunate to have met Prof. Tom Schrijvers from KU Leuven, talked to him
about my work and found a topic of mutual interest, which led to one of the key publications
for this PhD thesis. I learned a lot from you! I would also like to thank my co-advisor Prof.
T.H. Tse for his valuable suggestions and guidelines. He helped revise my papers and thisthesis, continuously nudging me to make my writing clearer. His feedback is nothing shortof inspiring, which I greatly enjoyed. During the years I spent at HKU, I had the opportunity
to collaborate with a number of excellent fellow researchers, to name a few (in no particularorder): Tomas Tauber, Zhiyuan Shi, Weixin Zhang, Huang Li, Yanpeng Yang, Ningning Xie,
Yanlin Wang and Haoyuan Zhang. Especially my coauthor Ningning Xie, who, during mylow days, offered me to collaborate on a project of her. This collaboration kept me busy foriii

a while, occupying the time I would otherwise spend on indulging in the gloomy outlook of
my research life. Our fruitful collaboration is still going on as of this writing.
Last but not least, I would love to thank my family for being extremely supportive throughout my studies. I made it through thanks to all of you.

iv

Contents
Declarationi

Acknowledgmentsiii

List of Figuresxi

List of Tables

xiii

I

Prologue

Introduction

1.1

Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3

1.1.1

First-Class Classes . . . . . . . . . . . . . . . . . . . . . . . . . . .

5

1.1.2

(First-Class) Mixins and Traits . . . . . . . . . . . . . . . . . . . .

5

1.1.3

Family Polymorphism and Nested Composition . . . . . . . . . . .

7

Our Proposed Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7

1.2.1

Disjoint Intersection Types . . . . . . . . . . . . . . . . . . . . . .

8

1.3

Contributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9

1.4

Organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12

1.2



Background



2.1

Intersection Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

15

2.1.1

The Merge Operator . . . . . . . . . . . . . . . . . . . . . . . . . .

17

2.1.2

(In)Coherence . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18

2.1.3

Disjoint Intersection Types . . . . . . . . . . . . . . . . . . . . . .

20

2.1.4

Disjoint Polymorphism . . . . . . . . . . . . . . . . . . . . . . . .

22

2.2

Mixins and Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

23

2.3

Family Polymorphism and Nested Composition . . . . . . . . . . . . . . .

25

2.4

Functional Object Encodings . . . . . . . . . . . . . . . . . . . . . . . . .

28

2.5

Program Equivalence and Logical Relations . . . . . . . . . . . . . . . . .

30v

Contents

II

Typed CalculiSemantics of the λ+i Calculus



3.1

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

3.2

λ+
i by Examples. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

36
36

3.3

3.2.1 The Expression Problem, λ+i Style . . . . . . . . . . . . . . . . . .
+Syntax and Semantics of λi . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.1

Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

3.3.2

Declarative Subtyping . . . . . . . . . . . . . . . . . . . . . . . . .

41

3.3.3

Typing of λ+i . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

42

Syntax and Semantics of λco . . . . . . . . . . . . . . . . . . . . . . . . . .

43

3.4.1

Explicit Coercions and Coercive Subtyping . . . . . . . . . . . . .

44

3.4.2

Typing of λco. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

3.4.3

Dynamic Semantics . . . . . . . . . . . . . . . . . . . . . . . . . .

45

3.4.4

Elaboration Semantics . . . . . . . . . . . . . . . . . . . . . . . . .

46

3.5

Comparison with λi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

3.6

Algorithmic Subtyping . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

48

3.6.1

The Subtyping Algorithm . . . . . . . . . . . . . . . . . . . . . . .

49

3.6.2

Correctness of the Algorithm . . . . . . . . . . . . . . . . . . . . .

50

3.4



Semantics of the F+
i Calculus



4.1

Motivation: Compositional Programming . . . . . . . . . . . . . . . . . .

56

4.1.1

A Finally Tagless Encoding in Haskell . . . . . . . . . . . . . . . .

56

4.1.2

The F+i Encoding. . . . . . . . . . . . . . . . . . . . . . . . . . .

59

4.2

Syntax and Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

62

4.3

Disjointness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

66

4.4

Elaboration and Type Safety . . . . . . . . . . . . . . . . . . . . . . . . . .

68

4.5

Algorithmic System and Decidability . . . . . . . . . . . . . . . . . . . . .

71

4.5.1

Algorithmic Subtyping Rules . . . . . . . . . . . . . . . . . . . . .

72

4.5.2

Decidability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

73

III Coherence


vi

40



Coherence for λ+
i



5.1

77

The Intuition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Contents
5.2

In Search of Coherence . . . . . . . . . . . . . . . . . . . . . . . . . . . .

78

5.2.1

Expression Contexts and Contextual Equivalence. . . . . . . . . . .

79

5.2.2

λ+
i Contexts and Refined Contextual Equivalence. . . . . . . . . .

80

5.3

The Canonicity Relation, Formally Defined . . . . . . . . . . . . . . . . . .

81

5.4

Establishing Coherence . . . . . . . . . . . . . . . . . . . . . . . . . . . .

84

5.5

Some Interesting Corollaries . . . . . . . . . . . . . . . . . . . . . . . . . .

86

Coherence for F+
i



6.1

The Challenge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

87

6.2

Impredicativity and Disjointness at Odds . . . . . . . . . . . . . . . . . . .

88

6.3

The Canonicity Relation for F+i. . . . . . . . . . . . . . . . . . . . . . . .

89

6.4

Establishing Coherence . . . . . . . . . . . . . . . . . . . . . . . . . . . .

91

IV ApplicationsFirst-Class Traits



7.1

Motivation: First-Class Classes and Dynamic Inheritance . . . . . . . . . .

95

7.2

Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

97

7.2.1

First-Class Classes in JavaScript . . . . . . . . . . . . . . . . . . . .

97

7.2.2

A Glance at Typed First-Class Traits in SEDEL . . . . . . . . . . . 101

7.3

7.4



Typed First-Class Traits . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
7.3.1

Traits in SEDEL . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

7.3.2

Two Roles of Traits in SEDEL . . . . . . . . . . . . . . . . . . . . . 106

7.3.3

Trait Types and Trait Requirements . . . . . . . . . . . . . . . . . . 107

7.3.4

Traits with Parameters and First-Class Traits . . . . . . . . . . . . . 108

7.3.5

Detecting and Resolving Conflicts in Trait Composition . . . . . . 109

7.3.6

Disjoint Polymorphism and Dynamic Composition . . . . . . . . . 111

Formalizing Typed First-Class Traits . . . . . . . . . . . . . . . . . . . . . 111
7.4.1

Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

7.4.2

Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

7.4.3

Type Soundness and Coherence . . . . . . . . . . . . . . . . . . . 118

Case Study: Modularizing Language Components



8.1

Object Algebras and Extensible Visitors in SEDEL . . . . . . . . . . . . . . 121

8.2

Dynamic Object Algebra Composition Support . . . . . . . . . . . . . . . 124

8.3

Case Study Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125

vii

Contents
8.4

Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

V

Related and Future Work



Related Work



9.1

Coherence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
9.1.1

Normalization-based Approach . . . . . . . . . . . . . . . . . . . . 131

9.1.2

Context-based Approach . . . . . . . . . . . . . . . . . . . . . . . 132

9.2

BCD Subtyping and Decidability . . . . . . . . . . . . . . . . . . . . . . . 132

9.3

Intersection types, Merge Operator and Polymorphism . . . . . . . . . . . 134

9.4

Row polymorphism and bounded polymorphism . . . . . . . . . . . . . . 135

9.5

Typed First-Class Classes/Mixins/Traits . . . . . . . . . . . . . . . . . . . . 136

9.6

Mixin-Based Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

9.7

Trait-Based Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137

9.8

Family Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

9.9

Languages with More Advanced Forms of Inheritance . . . . . . . . . . . . 139

9.10 Module Systems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
 Future Work

10.1 Categorical Semantics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
10.1.1 Properties of Intersection Types . . . . . . . . . . . . . . . . . . . 141
10.1.2 Connecting with Disjointness . . . . . . . . . . . . . . . . . . . . . 143
10.1.3 Interpretation of Intersection Types . . . . . . . . . . . . . . . . . 144
10.1.4 Interpretation of Disjoint Intersection Types . . . . . . . . . . . . . 145
10.1.5 Coherence, from the Categorical Perspective . . . . . . . . . . . . 146
10.2 Implicit Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
10.2.1 Declarative Subtyping . . . . . . . . . . . . . . . . . . . . . . . . . 146
10.2.2 Disjointness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
10.2.3 Declarative Typing . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
10.2.4 Algorithmic System . . . . . . . . . . . . . . . . . . . . . . . . . . 149
10.3 Disjoint Polymorphism vs. Row Polymorphism . . . . . . . . . . . . . . . 149
10.4 Recursive Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
10.5 Other Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
10.5.1 Union Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
10.5.2 Nominal Typing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
10.5.3 Mutable State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154

viii

Contents

VI Epilogue Conclusion

Bibliography



VII Technical Appendix



A Circuit Embeddings

B Decidability

C Proofs about SEDEL



D λ+
i Typing Rules, in Full

D.1 λco Typing Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
E F+
i Typing Rules, in Full
E.1



Fco Typing Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205

F SEDEL Typing Rules, in Full



ix

List of Figures
2.1

Type system of λi. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21

2.2

Multiple inheritance and mixins . . . . . . . . . . . . . . . . . . . . . . . .

24

2.3

Traits and conflicts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

25

2.4

The expression problem, Scandinavian style . . . . . . . . . . . . . . . . .

27

3.1

Summary of the relationships between language components . . . . . . . .

40

3.2

Syntax of λ+
i

41

3.3

Declarative subtyping of λ+i. . . . . . . . . . . . . . . . . . . . . . . . . .

42

3.4

43

3.5

Bidirectional type system of λ+i . . . . . . . . . . . . . . . . . . . . . . . .
+Disjointness rules of λi . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.6

Syntax of λco . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

44

3.7

Coercion typing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

46

3.8

Dynamic semantics of λco . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

3.9

Algorithmic subtyping of λ+i. . . . . . . . . . . . . . . . . . . . . . . . .

49

3.10 Example derivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

51

3.11 Meta-functions of coercions . . . . . . . . . . . . . . . . . . . . . . . . . .

51

4.1

Two finally tagless embeddings of circuits. . . . . . . . . . . . . . . . . . .

57

4.2

60

4.3

Two F+
i embeddings of circuits. . . . . . . . . . . . . . . . . . . . . . . . .
Syntax of F+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
i

4.4

Well-formedness of types and contexts . . . . . . . . . . . . . . . . . . . .

63

4.5

Declarative subtyping of F+i. . . . . . . . . . . . . . . . . . . . . . . . . .

64

4.6

65

4.7

Bidirectional type system of F+i . . . . . . . . . . . . . . . . . . . . . . . .
+Disjointness rules of Fi . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4.8

Syntax of Fco . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

68

4.9

Typing rules of Fco . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

70

4.10 Dynamic semantics of Fco . . . . . . . . . . . . . . . . . . . . . . . . . . .

71

4.11 Algorithmic subtyping of F+. . . . . . . . . . . . . . . . . . . . . . . . .
i

72

4.12 Meta-functions of coercions, extended . . . . . . . . . . . . . . . . . . . .

73. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

44

62

67

xi

List of Figures

xii

5.1

Expression contexts of λco and λ+. . . . . . . . . . . . . . . . . . . . . .
i

79

5.2

λ+
i context typing (excerpt)

81

5.3

The canonicity relation for λ+i. . . . . . . . . . . . . . . . . . . . . . . .

82

6.1

The canonicity relation for F+i. . . . . . . . . . . . . . . . . . . . . . . .

90

6.2

Expression contexts of Fco and F+i

7.1

SEDEL core syntax and syntactic abbreviations . . . . . . . . . . . . . . . 112

7.2

Well-formedness and subtyping of SEDEL . . . . . . . . . . . . . . . . . . 113

7.3

Disjointness rules of SEDEL

7.4

Typing rules of SEDEL

8.1

Mini-JS expressions, values, and types . . . . . . . . . . . . . . . . . . . . 125

9.1

Summary of intersection calculi . . . . . . . . . . . . . . . . . . . . . . . . 135. . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . .

92. . . . . . . . . . . . . . . . . . . . . . . . . 114. . . . . . . . . . . . . . . . . . . . . . . . . . . . 116

List of Tables
3.1

Correspondence between coercions and terms . . . . . . . . . . . . . . . .

45

4.1

Correspondence between coercions and terms, extended . . . . . . . . . .

69

8.1

Overview of the languages assembled . . . . . . . . . . . . . . . . . . . . . 126

8.2

SLOC statistics: SEDEL implementation vs. vanilla AST implementation . 127

xiii

Part I
Prologue1 Introduction
This thesis investigates disjoint intersection types—a variant of intersection types—focusing
on its theoretical foundation and applications in the context of object-oriented programming. The results are three new typed calculi, the first two being core calculi and the last
one a source calculus, combining the power of parametric polymorphism, a rich subtyping
relation with the fine-grained expressiveness of disjoint intersection types. The key contribution of the thesis is that it unifies ideas that are seemingly unrelated but powerful on theirown in object-oriented programming—dynamic inheritance, first-class traits, family polymorphism, extensible design patterns—by a single lightweight mechanism, thus providing
new insights into software modularity and extensibility.

. Motivation
Programs are hard to write. It was so 50 years ago at the time of the so-called software crisis [Naur and Randell 1969]; it still remains so nowadays, as the software we use daily isgetting more and more complex and harder to maintain. Over the years, we have learned—
the hard way—that software should be constructed in a modular way, i.e., as a network ofsmaller and loosely connected modules. To facilitate writing modular code, researchers andsoftware practitioners have developed new methodologies; new programming paradigms;
more expressive type systems; as well as better tooling support. Still, this is not enough tocope with today’s needs. We will mention some limitations of existing mainstream languages
on supporting modular programming shortly. But before that, let us identify the following
well-established requirements for construction of modular software:
1. Extensibility in both dimensions: Extensions may require new variants to the datatype
and new operations on the datatype.
2. Strong static type safety: Extensions cannot cause run-time type errors.
3. No modification or duplication: Existing code must not be modified nor duplicated.
4. Separate compilation and type-checking: Safety checks or compilation steps must not
be deferred until linking or at run time.

3

1 Introduction
5. Independent extensibility: Independently developed extensions should be composable so that they can be used jointly.
6. Scalability: Extension should be scalable. The amount of code needed should be proportional to the functionality added.
7. Non-destructive extension: The base system should still be available for use within
the extended system.
The first four of these requirements correspond to Wadler’s expression problem [Wadler1998]. Zenger and Odersky [2005] added the 5th requirement. The last two requirements
were proposed by Nystrom et al. [2006]. Scalability (6th ) is often but not necessarily satisfied by separate compilation; it is important for extending large software. Non-destructive
extension (7th ) is an important requirement for legacy and performance reasons: it enables
clients of the extended system to reuse code and data of the base system, allowing some interoperability between new functionality and legacy code. To address the requirements, manysolutions have been proposed over the years (for example, see Oliveira [2009]; Oliveira andCook [2012]; Swierstra [2008]; Wang and Oliveira [2016]; Zenger and Odersky [2005], tocite a few). They differ considerably in the language context with varying degrees of extensibility they offer, as well as the limitations they impose. Building on the previous solutions,this thesis proposes a lightweight language design that addresses all of these requirements.
Various programming language features support modular programming, with varying
degrees of limitations. Functional languages, notably ML and OCaml, use module systems [MacQueen 1984] for flexible program construction. In particular, ML “functors”—
which are functions over modules—allow one to develop and compile a module independently from the modules on which it depends. One functor can then be instantiated withmultiple different modules during the execution of the program, enabling a powerful formof code reuse. One prominent weakness of ML modules (at least in current module implementations) is that they cannot be defined recursively, that is, mutually recursive functions
and datatypes must be written in the same module, even though they may belong conceptually to different modules. Another limitation is that modules form a separate, higher-orderfunctional language on top of the core and therefore ML is actually two languages in one.
Moreover, module systems usually put more emphasis on supporting data abstraction, whichadds considerable complexity to languages adopting module systems as the primary way toconstruct modular programs.
Object-oriented languages, on the other hand, use classes and inheritance as primarymechanisms to support code extensibility and reuse. Single inheritance found in mainstream41.1 Motivation
object-oriented languages (such as Java or C++) is perhaps the most well-known and wellstudied mechanism. However, programmers have long realized that single inheritance is
not flexible enough when it comes to structuring a class hierarchy: it works for small andsimple extensions, but does not work well for larger software systems such as compilers andoperating systems. There has been great interest in the past several years in mechanisms
for providing greater extensibility in object-oriented languages. Of particular relevance tothe subject of this thesis are three powerful linguistic mechanisms for software extensibility,
providing increasing order of flexibility, as well as complexity: first-class classes [Takikawa
et al. 2012], (first-class) mixins/traits [Bracha and Cook 1990; Schärli et al. 2003], and family
polymorphism [Ernst 2001].

.. First-Class Classes
Many dynamically typed languages (including JavaScript, Ruby, Python or Racket) support
first-class classes. In those languages classes are first-class values and, like any other values,
they can be passed as an argument, or returned from a function. Furthermore, first-classclasses support dynamic inheritance: i.e., they can inherit from other classes at run time, enabling programmers to abstract over the inheritance hierarchy. In contrast, most statically
typed languages do not have first-class classes and dynamic inheritance. While all statically
typed object-oriented languages allow first-class objects (i.e., objects can be passed as arguments and returned as results), the same is not true for classes. Classes in languages suchas Scala, Java or C++ are typically a second-class construct, and the inheritance hierarchy isstatically determined.
Despite the popularity and expressive power of first-class classes in dynamically typed languages, there is surprisingly little work on typing of first-class classes. First-class classes anddynamic inheritance pose well-known difficulties in terms of typing. For example, in his thesis, Bracha [1992] comments several times on the difficulties of typing dynamic inheritanceand first-class mixins, and proposes the restriction to static inheritance that is also common
in statically typed languages. One of the motivations for this thesis is to propose a type discipline that can encode first-class classes. Moreover, we push this one step further: for the
first time, this thesis shows how to encode first-class traits in a statically typed setting. But
first things first, let us briefly explain what are traits, and the related concept “mixins”.

.. (First-Class) Mixins and Traits
As remarked earlier, single inheritance is inadequate and inflexible to write large software.
To overcome this limitation, multiple inheritance was proposed as a generalization of single

5

1 Introduction
inheritance. However, multiple inheritance is renowned for being tricky to get right, largely
because of the possible ambiguity issues that arise when conflicting features are inherited
along different paths. Mixins [Bracha and Cook 1990] provide a simple mechanism for multiple inheritance without the ambiguity issue. A mixin is a class declaration parameterized
over a superclass, able to extend a variety of parent classes with the same set of features. Mixins are composed linearly, and that methods defined in mixins appearing later override all
the identically named methods of earlier mixins. Because of the linear order of composition,a class may not be able to access a member of a given super-mixin because the member isoverridden by another mixin.
Traits [Ducasse et al. 2006; Schärli et al. 2003] are an alternative to mixins, and other models of multiple inheritance. The key difference between traits and mixins lies on the treatmentof conflicts when composing multiple traits/mixins. Mixins adopt an implicit resolution
strategy for conflicts, where the compiler automatically picks one implementation in caseof conflicts. Traits, on the other hand, employ an explicit resolution strategy, where the compositions with conflicts are rejected, and the conflicts are explicitly resolved by programmers.
Schärli et al. [2003] make a good case for the advantages of the trait model. In particular,
traits avoid bugs that could arise from accidental conflicts that were not noticed by programmers. With the mixin model, such conflicts would be silently resolved, possibly resulting in
unexpected run-time behavior due to a wrong method implementation choice. From a modularity point of view, the trait model also ensures that composition is commutative, thus theorder of composition is irrelevant and does not affect the semantics. Bracha [1992] claims
that “The only modular solution is to treat the name collisions as errors...”, strengthening thecase for the use of a trait model of composition. Otherwise, if the semantics is affected by theorder of composition (like in the mixin model), global knowledge about the full inheritancegraph is required to determine which implementations are chosen.
Mixins and traits as found in most statically typed languages/calculi are typically a secondclass construct. Promoting mixins/traits to first-class citizens adds considerable expressiveness and flexibility in terms of software extensibility, as will be illustrated throughout thisthesis. Only recently some progress has been made in statically typing first-class classes anddynamic inheritance [Lee et al. 2015; Takikawa et al. 2012]. However, prior to this thesis,no previous work supports typed first-class traits. A key challenge, compared to models with
first-class classes or mixins, is how to detect conflicts at compile time even when not knowing all components being composed statically. This is important because in the setting withdynamic inheritance and polymorphism, the possibility of accidental conflicts caused by programmers is extremely high.

61.2 Our Proposed Solution

.. Family Polymorphism and Nested Composition
The last mechanism—also the most powerful and complex one—is family polymorphism.
In family polymorphism [Ernst 2001], inheritance is extended to work on a whole familyof classes, rather than just a single class. This enables high degrees of modularity and code
reuse, enabling simple solutions to hard programming language problems, like the expression problem [Wadler 1998]. An essential feature of family polymorphism is nested composition [Corradi et al. 2012; Ernst et al. 2006; Nystrom et al. 2004], which allows the automatic
inheritance/composition of nested (or inner) classes when the enclosing classes are composed. Nystrom et al. [2004] call this scalable extensibility: “the ability to extend a body ofcode while writing new code proportional to the differences in functionality”.
Not many mechanisms that support family polymorphism are available in existing mainstream languages. The Cake pattern [Odersky and Zenger 2005; Zenger and Odersky 2005]
in Scala provides some form of family polymorphism. In order to model this modest formof family polymorphism, this pattern uses virtual types, self types, path-dependent types andstatic mixin composition. Even with so many sophisticated features, composition of families is still quite heavyweight and manual. The problem is due to the lack of deep mixin
composition. Though solutions do exist [Oliveira et al. 2013], they usually require lowlevel type-unsafe programming features such as dynamic proxies, reflection or other metaprogramming techniques. It is known that designing a sound type system that fully supports
family polymorphism and nested composition is notoriously hard; there are only a few, quite
sophisticated, research languages that manage this [Clarke et al. 2007; Ernst et al. 2006; Nystrom et al. 2004; Saito et al. 2007]. But those mechanisms usually focus on getting a relatively
complex Java-like language with support for family polymorphism. Instead, one of the motivations for the work presented in this thesis is to come up with a minimal calculus that
supports nested composition.

. Our Proposed Solution
This thesis sets out to explore an alternative object-oriented language design that makes it
easy and safe to extend and compose existing code on the language level. More specifically, we seek to rein in ideas that are seemingly unrelated but powerful in object-oriented
programming—dynamic inheritance, first-class traits, family polymorphism—under a simple unifying mechanism: they are but different manifestations of a single underlying typediscipline: disjoint intersection types. Through a serious of examples and rigorous analysis
in this thesis, we hope to convince readers that disjoint intersection types are a feasible semantic tool to facilitate code reuse and modularity. In particular, for family polymorphism,

7

1 Introduction
we show that the combination of the merge operator and a rich subtyping relation captures
the essence of nested composition; for traits, we show that the merge operator and disjoint
intersection types enable encodings of typed first-class traits. Combined with parametricpolymorphism, we can further express a very dynamic form of mixin-style compositions,enabling programmers to write highly modular and reusable software components.
So what are disjoint intersecting types? Here only highlights are given—more details areto be delivered in later chapters.

.. Disjoint Intersection Types
One recurring theme of this thesis are intersection types (usually written A & B). Intersection
types [Coppo and Dezani-Ciancaglini 1978; Pottinger 1980] have a long history in programming languages. They were originally introduced to characterize exactly all strongly normalizing lambda terms. Since then, starting with Reynolds’s work on Forsythe [Reynolds 1988],
they have also been employed to express useful programming language constructs, such as
key aspects of multiple inheritance [Compagnoni and Pierce 1996] in object-oriented programming. One notable example is the Scala language [Odersky et al. 2004] and its DOT
calculus [Amin et al. 2012], which make fundamental use of intersection types to express aclass/trait that extends multiple other traits. Other modern programming languages, such as
TypeScript [Microsoft 2012], Flow [Facebook 2014] and Ceylon [Redhat 2011], also adopt
some form of intersection types.
Intersection types come in different varieties in the literature. A far more common formof intersection types are the so-called refinement types [Davies and Pfenning 2000; Dunfield
and Pfenning 2003; Freeman and Pfenning 1991]. Refinement types restrict the formation ofintersection types so that the two types in an intersection are refinements of the same simple(unrefined) type. Refinement intersections increase only the expressiveness of types (moreprecise properties can be checked) and not of terms. For this reason, Dunfield [2014] argues
that refinement intersections are unsuited for encoding various useful language features that
require the merge operator (or an equivalent term-level operator).
Unrestricted intersection types with a term-level “merge” operator as an explicit introduction form increase the expressiveness of the term language. This operator was introduced by
Reynolds in Forsythe [Reynolds 1988] and adopted by a few other calculi [Alpuim et al. 2017;
Castagna et al. 1992; Dunfield 2014; Oliveira et al. 2016]. Unfortunately, while the merge operator is powerful, it also makes it hard to get a coherent [Reynolds 1991] (or unambiguous)
semantics. As a first approximation, a semantics is said to be coherent if a valid program
has exactly one meaning (i.e., one value when run). Unrestricted uses of the merge operator
can be ambiguous, leading to an incoherent semantics where the same program can evaluate

81.3 Contributions
to different values. We shall come back to this form of intersection types in more details in
Section 2.1.
Recently, Oliveira et al. [2016] proposed λi : a calculus with a variant of intersection types
called disjoint intersection types. Calculi with disjoint intersection types also feature themerge operator, with restrictions that all expressions in a merge operator must have disjoint
types and all well-formed intersections are also disjoint. With the disjointness restrictions, λi
is proved to be coherent. As shown by Alpuim et al. [2017], calculi with disjoint intersection
types are very expressive and can be used to statically type-check JavaScript-style programs
using mixins. Yet they retain both type safety and coherence. While coherence may seem atfirst of mostly theoretical relevance, it turns out to be very relevant for object-oriented programming. As remarked earlier, a key issue for multiple inheritance is ambiguity caused byconflicting features inherited from different parents. Disjoint intersection types enforce thatthe types of parents are disjoint and thus that no conflicts exist. Any violations are staticallydetected and can be manually resolved by the programmer (for example by dropping one of
the conflicting field/methods from one of the parents). This is very similar to existing trait
models [Ducasse et al. 2006; Schärli et al. 2003]. Therefore in an object-oriented language
modeled on top of disjoint intersection types, coherence implies that no ambiguity arises
from multiple inheritance. This makes reasoning a lot simpler.
The main goal of this thesis is to significantly increase the expressiveness of disjoint intersection types by extending the simple forms of multiple inheritance/composition supported
by previous work [Alpuim et al. 2017; Oliveira et al. 2016] into a more powerful form supporting nested composition and parametric polymorphism. On the pragmatic side, the outcome is a programming language with support for first-class traits, dynamic inheritance andnested composition. On the theoretical side, we put disjoint intersection types on a solid
footing by thoroughly exploring their meta-theoretical properties.

. Contributions
In this thesis, we present three new typed calculi, starting from a simple calculus with disjoint intersection types, then adding parametric polymorphism and finally ending up with arelatively sophisticated object-oriented language with support for first-class traits, dynamic
inheritance and nested composition.
+
The λ+
i calculus. The first one, named λi , is a simple calculus with records and disjoint

intersection types that supports nested composition. The essential novelty of λ+i is the adoption of the Barendregt, Coppo and Dezani (BCD) subtyping [Barendregt et al. 1983], which

91 Introduction
includes distributivity rules between function/record types and intersection types. These
rules are the delta that enables extending the simple forms of multiple inheritance/composition supported by previous work [Oliveira et al. 2016] into a more powerful form supporting
nested composition. The incorporation of BCD subtyping is highly challenging for two different reasons. The first difficulty is how to preserve coherence. Although previous work on
disjoint intersection types proposes a solution to coherence, the solution imposes several adhoc restrictions to guarantee the uniqueness of the elaboration and thus allows for a simple
syntactic proof. However such restrictions make it hard or impossible to adapt the proof toextensions of the calculus with distributivity rules. To deal with coherence, a more semantic proof method, called the canonicity relation, is employed. The second difficulty is that
BCD subtyping is non-algorithmic: the presence of a transitivity axiom in the rules makes
it hard to get an algorithmic version. To address it, we adapt and extend Pierce’s decision
procedure [Pierce 1989] (closely related to BCD) with subtyping of records and coercions,and propose an equivalent algorithmic subtyping relation.
+
The F+
i calculus. The second one, named Fi , is a polymorphic calculus with disjoint+
intersection types. F+
i is essentially λi enriched with a variant of parametric polymor-

phism called disjoint polymorphism [Alpuim et al. 2017]. The addition of parametric poly+morphism greatly increases the expressiveness of λ+i : Fi improves upon the finally tag-less [Carette et al. 2009] and object algebra [Oliveira and Cook 2012] approaches and support advanced compositional designs, and enables the development of highly modular andreusable programs. F+
i is a generalization and extension of the Fi calculus [Alpuim et al.
2017], which proposed the idea of disjoint polymorphism. The main novelty of F+
i is a novel
subtyping algorithm with distributivity laws. Distributivity plays a fundamental role in improving compositional designs, by enabling the automatic composition of multiple operations/interpretations. The main technical challenge is the proof of coherence as impredicativity makes it hard to develop a well-founded logical relation for coherence. However,by restricting the system to predicative instantiations only we are able to develop a suitable
logical relation and show coherence. Besides coherence, we show several other importantmeta-theoretical results, such as type-safety, sound and complete algorithmic subtyping, anddecidability of the type system. Remarkably, unlike F<: ’s bounded polymorphism [Cardelliand Wegner 1985], disjoint polymorphism in F+
i supports decidable type-checking.
Typed first-class traits. Lastly we present the design of SEDEL: a polymorphic language with first-class traits, supporting parametric polymorphism, dynamic inheritance as wellas conventional object-oriented features such as dynamic dispatching and abstract methods.

10

1.3 Contributions
Traits pose additional challenges when compared to models with first-class classes or mixins, because method conflicts should be detected statically, even in the presence of features
such as dynamic inheritance and parametric polymorphism. To address the challenges oftyping first-class traits and detecting conflicts statically, SEDEL adopts the well-establishedapproach of elaborating high-level language constructs to a low-level core calculus. The main
contribution of SEDEL is to show how to model source language constructs for first-class+traits and dynamic inheritance. The work on λ+i and Fi aimed at core record calculi, andomits important features for practical object-oriented languages, including (dynamic) inheritance, dynamic dispatching and abstract methods. Based on Cook’s work on the denotational semantics for inheritance [Cook 1989; Cook and Palsberg 1989], we show how to+design a source language that is elaborated into F+
i . SEDEL’s elaboration into Fi is proved

to be both type-safe and coherent. Coherence ensures that the semantics of SEDEL is unambiguous. In particular this property is useful to ensure that programs using traits are free ofconflicts/ambiguities (even when the types of the object parts being composed are not fully
statically know). We illustrate the applicability of SEDEL with several example uses for firstclass traits. Furthermore, we conduct a case study that modularizes programming language
interpreters using a highly modular form of Visitors [Oliveira 2009; Torgersen 2004].
In summary the contributions of this thesis are:
• We present λ+i , a calculus with disjoint intersection types that features both BCD-style
subtyping and the merge operator. This calculus is both type-safe and coherent, andsupports nested composition.
+• We present F+
i , a polymorphic calculus with disjoint intersection types. Fi is incor-

porated with a BCD-like subtyping relation extended with disjoint polymorphism. F+iis both type-safe and coherent, and supports nested composition.
• We present SEDEL, an object-oriented language design that supports typed first-classtraits, dynamic inheritance, as well as standard object-oriented features such as dynamic dispatching and abstract methods. We show how the semantics of SEDEL canbe defined by elaboration into F+i .
• A more flexible notion of disjoint intersection types where only merges need to bechecked for disjointness. This removes the need for enforcing disjointness for all wellformed types, making calculi with disjoint intersections more easily extensible.
• The canonicity relation: a powerful proof method for establishing coherence of calculi
with disjoint intersection types, BCD-like subtyping and polymorphism.

111 Introduction• A comprehensive Coq mechanization of all metatheory, including type safety, coherence, algorithmic soundness and completeness, etc.1 This has notably revealed several
missing lemmas and oversights in Pierce’s manual proof of BCD’s algorithmic subtyping [Pierce 1989]. As a by-product, we obtain the first mechanically verified BCD-style
subtyping algorithm with coercions.
• A full-blown implementation of SEDEL; it runs and type-checks all the examples inthis thesis. We also conduct a case study, which shows that support for composition of
object algebras [Oliveira and Cook 2012] is greatly improved in SEDEL. Using such
improved design patterns we re-code the interpreters from an undergraduate textbook
on programming languages [Cook 2013] in a modular way. The implementation, Coq
formalization and all code presented in this thesis are available at https://github.
com/bixuanzju/phd-thesis-artifact.

. Organization
We begin with some background in the main topics of this thesis in Chapter 2 in order tokeep this thesis as self-contained as possible and also to put our methods and contributions
into context. The structure of the technical content in the thesis is divided into three parts:+
Part II: Chapters 3 and 4 formally define the type systems of λ+i and Fi , respectively. We

first give the syntax and semantics of the two calculi. The semantics is defined in two
parts. The “target” languages are two standard type systems (simply-typed lambda
calculus and System F, respectively) that do not have intersection types, the merge
operator or subtyping. The “source” languages, defined by translation into the target languages, contain intersection types, the merge operator and subtyping. We thenprove some basic properties such as type safety of the elaboration, soundness and completeness of the algorithmic subtyping, etc.
Part III: Chapters 5 and 6 explore the issue of coherence. In Chapter 5 we first propose asemantically founded definition of coherence. We then propose a proof method called
the canonicity relation to establish coherence of λ+i . In Chapter 6 we follow the same
technique in Chapter 5 but encounter a severe issue of impredicativity. We impose apredicativity restriction and adapt the canonicity relation to establish coherence of F+
i .
1

R

For convenience, whenever possible, definitions, lemmas and theorems have hyperlinks (click) to their++
Coq counterparts. Also since F+
completely
subsumes
λ
,
to
save
work,
for
λ
metatheory
weprovidecrossiiireferences to the corresponding F+
Coqdefinitions,instead.
i121.4 Organization
Part IV: In Chapter 7 we present the syntax and semantics of SEDEL. In particular we show
how to elaborate source-level constructs for first-class traits into expressions of F+i . InChapter 8 we conduct a case study of modularizing programming language featuresusing a highly modular form of Visitors.
Chapter 9 reviews related work, Chapter 10 discusses future work and Chapter 11 concludes.
This thesis is largely based on two publications [Bi and Oliveira 2018; Bi et al. 2018] by theauthor and one draft [Bi et al. 2019], currently under review as of this writing. In comparison
to the original publications, this thesis contains a more in-depth and consistent treatment ofdisjoint intersection types.
Chapters 3 and 5: Xuan Bi, Bruno C. d. S. Oliveira, and Tom Schrijvers. 2018. “The Essenceof Nested Composition”. In European Conference on Object-Oriented Programming(ECOOP).
Chapters 4 and 6: Xuan Bi, Ningning Xie, Bruno C. d. S. Oliveira, and Tom Schrijvers. 2019.
“Distributive Disjoint Polymorphism for Compositional Programming”. Submitted toEuropean Symposium on Programming (ESOP).
Chapters 7 and 8: Xuan Bi and Bruno C. d. S. Oliveira. 2018. “Typed First-Class Traits”. In
European Conference on Object-Oriented Programming (ECOOP).

This thesis assumes familiarity with basic knowledge of programming language theory andobject-oriented programming. We recommend Pierce’s excellent textbook on programming
languages [Pierce 2002] for a general introduction.

13

 Background
This chapter sets the stage for the three typed calculi that will be presented in later chapters by
expanding upon some relevant topics from the introduction. In Section 2.1 we start with thetraditional formulation of intersection types, followed by an introduction of the merge operator and the issue of coherence. We then review the λi calculus [Oliveira et al. 2016], the first
calculus featuring disjoint intersection types, and briefly discuss how disjointness achieves
coherence. In Section 2.2 we review the concepts of mixins and traits, their drawbacks andstrengths. In Section 2.3 we introduce family polymorphism by means of presenting Ernst’s
elegant solution [Ernst 2004] to the expression problem. Section 2.4 reviews the denotational model of inheritance. Finally in Section 2.5 we give a simple introduction to program
equivalence and logical relations.

. Intersection Types
Intersection types in the pure lambda calculus were developed in the late 1970s by Coppo andDezani-Ciancaglini [1978], and independently by Pottinger [1980]. The original motivation
for intersection types was to devise a type-assignment system à la Curry [Curry and Feys1958] that satisfies the following two properties:
1. The typing of a term should be preserved under β-conversion. (Under Curry’s system,
β-reduction preserves types but β-expansion, in general, does not.)2. Every (strongly) normalizable term has a meaningful type. (We refer the reader totheir paper for a precise definition of “meaningful”.)
The idea of intersection types is remarkably simple and natural. From the set-theoretic
perspective, an intersection type A & B for every pair of types A and B is thought of as containing all the elements of A that are also elements of B; from the type-theoretic point ofview, A & B is a subtype of A, as well as of B; from the order-theoretic point of view, A & B isa greatest lower bound of A and B.1 In the literature of object-oriented programming, intersection types are long known to model multiple inheritance [Compagnoni and Pierce 1996].
1

Note that we say “a” rather than “the” because greatest lower bounds are not unique, but they are all “equal” toA & B in a sense that will be made precise in Section 10.1.

15

2 Background
The intuition is that if we read the subtyping A <: B as “A is a subclass of B”, then A & B is a“name” of a class with all the common properties of A and B. Of course, this analog is not exact, in the same sense that inheritance is not subtyping [Cook et al. 1989]. But it is intuitively
appealing, and as we will see, can be made more precise in a sufficiently enriched calculus
based on intersection types. More pragmatically, many programming languages, such as
Scala, TypeScript, Flow and Ceylon adopt some form of intersection types. For example, in
Scala we can express a class A implements both B and C by the following declaration:
class A extends B with C

where B with C denotes an intersection type between B and C.
Intersection subtyping. Three subtyping rules capture the order-theoretic properties ofintersection types:
S-interL

S-interR

A & B <: A

A & B <: B

S-inter

C <: A

C <: B

C <: A & B

Two nice consequences follow:
1. The top type ⊤ can be regarded as the 0-ary form of intersection. It is a maximum
element of the subtyping ordering, i.e., A <: ⊤ for every type A.
2. Multi-field record types can be thought of as an intersection of single-field record
types. Thus, instead of{l1 : A1 , ... , ln : An }we can write{l1 : A1 } & ... & {ln : An }Note that the width and depth subtyping of records become a consequence of intersection subtyping.
Distributivity rules. Two additional subtyping rules are usually found in the literatureof intersection types (e.g., see Barendregt et al. [1983]; Reynolds [1988]). The first one captures the relation between intersections and function spaces, allowing intersections to distribute over the right-hand side of →’s:
S-distArr(A1 → A2 ) & (A1 → A3 ) <: A1 → A2 & A3

16

2.1 Intersection Types
Note that the other direction is also derivable (cf. Section 3.3). The second rule captures therelation between intersections and (singleton) records, allowing intersections to distributeover record labels:

S-distRcd{l : A} & {l : B} <: {l : A & B}
These two rules, though intuitively reasonable, will have a strong effect on both syntactic andsemantic properties of the language. For example, rule S-distArr implies that ⊤ <: A → ⊤for any A; and rule S-distRcd implies that ⊤ <: {l : ⊤}.

Intersection typing. The introduction rule of intersection types says that a term E canbe given type A & B if it inhabits both A and B:
interIE : A1

E : A2

E : A1 & A2
The corresponding elimination rule allows us to derive, given a derivation of E : A1 & A2 ,that E : A1 and E : A2 . But this already follows from intersection subtyping and the subsumption rule; so we need not to add the elimination rule explicitly to the calculus.

.. The Merge Operator
Intersection types were first incorporated into a practical programming language “Forsythe”
by Reynolds [1988, 1997], who used them to encode features such as operator overloading by means of a “merge” operator p1 , , p2 —“a construction for intersecting or ‘merging’
meanings” [Reynolds 1997, p. 24]. (Reynolds actually used single comma p1 , p2 , but here
we follow Dunfield by using double commas for consistency.) Reynolds demonstrated thepower of the merge operator by developing an encoding of records by using intersection
types; similar ideas also appear in Castagna et al. [1992]. The idea is to have only single-fieldrecords with the introduction form {l = E} of type {l : A} and the elimination form E.l(record projection). Thus instead of{l1 = E1 , ... , ln = En }we can write{l1 = E1 } , , ... , , {ln = En }17

2 Backgroundwhich plays nicely with the syntactic sugar of multi-field record types as an intersection of
single-field record types.
Recently, Dunfield [2014] developed a method for elaborating intersections and unions
into products and sums. Central to his system is a source-level merge operator E1 , , E2 ,reminiscent of Forsythe [Reynolds 1997], which embodies several computationally distinct
terms, and can be checked against various parts of an intersection type. In his system, theintroduction form of intersection types is still rule interI, and two additional rules for themerge operator are added:
mergeLmergeR

E1 : AE2 : A

E1 , , E2 : A

E1 , , E2 : A

In other words, a merge expression can choose to type one subterm and ignore the other. In
combination of rule interI, they allow to type check two distinct implementations E1 andE2 with completely different types A1 and A2 of the intersection. For example, let E1 = λx. x
and E2 = 1, then the type (Int → Int) & Int is inhabited by E1 , , E2 :E1 : Int → Int
E1 , , E2 : Int → Int

mergeLE2 : Int
E1 , , E2 : Int

E1 , , E2 : (Int → Int) & Int

mergeR
interI

Dunfield also developed elaboration typing rules which, given a source expression with unrestricted intersections and unions, type-check and transform the program into an ordinary
λ-calculus term with sums and products. For example, the expression (λx. x) , , 1 elaborates
to a pair ⟨λx. x, 1⟩. As usual, his system does not have explicit source-level intersection eliminations; elaboration puts all needed projections into the target program. For instance, thesame expression (λx. x) , , 1, when checked against Int, elaborates to π2 ⟨λx. x, 1⟩. The typedirected elaboration is elegant, type-safe, and serves as the original foundation for calculi
with disjoint intersection types.

.. (In)Coherence
While Dunfield’s system is simple and powerful, it has serious usability issues. More specifically, it lacks the theoretically and practically important property of coherence [Reynolds
1991]: the meaning of a target program depends on the choice of elaboration typing derivation. For example, the expression 1 , , true has type Int & Bool. It can be used either as an

18

2.1 Intersection Types
integer or a Boolean, the result is always clear (1 or true). However, when two types haveoverlapping components, it is not at all clear which value to pick. For example, the expression 1 , , 2 (when checked against Int) could elaborate to either 1 or 2, depending on theparticular choice in the implementation. Dunfield [2014] had a workaround by trying theleft part 1 first. It is equally acceptable that one can opt to choose the right part 2. But neither
is satisfying from a theoretical point of view.
To recover a coherent semantics, one could limit the merges according to their surface
syntax, as Reynolds did in Forsythe. But as Dunfield [2014] pointed out, “crafting an appropriate syntactic restriction depends on details of the type system, which is not robust as thetype system is extended”. Another simple idea would be to require all types in an intersection be distinct. This works fine for simple types such as Int and Bool: Int & Bool is clearlya good intersection. But it is less clear as to what constituents a “good” (read unambiguous)intersection type in general. A moment of thoughts leads to the following principle: goodintersection types are defined in terms of the subtyping relation. After all, it is the subtyping
relation that defines the behavior of intersection types. A first attempt would be to requirethat two types A and B can form an intersection if both types are not subtype of each other.
At first glance, this seems to be a reasonable definition because it rules out the problematicmerge 1 , , 2. However, it is still not enough. Consider the following expression (taken from
Oliveira et al. [2016]):(1 , , ‘‘c”) , , (2 , , true)
The first component (1 , , ‘‘c”) has type Int & String and the second component (2 , , true)
has type Int & Bool. It is clear that neither of the two is a subtype of the other. However,extracting an integer from the above expression is ambiguous (1 or 2).
When moving to richer types, it is even less clear how to deal with, for example, intersections of higher-order functions. Consider the following intersection types (again taken from
Oliveira et al. [2016]):
1. (Int → Int) & (String → String)
2. (String → Int) & (String → String)
3. (Int → String) & (String → String)
We can ask which of those intersection types are qualified as good. It seems reasonable toexpect the first one is good, since both the domain and range types are different. But theother two are not that obvious to see. Clearly a formal notion of well-behaved intersection
types are called for!

19

2 Background
The issue of coherence is addressed (with some compromises) by Oliveira et al. [2016] with
the notion of disjointness, as we will discuss next.

.. Disjoint Intersection Types
Disjoint intersection types, first introduced in the λi calculus [Oliveira et al. 2016] provide
a remedy for the coherence problem, by imposing restrictions on the uses of merges and on
the formation of intersection types. The syntax of λi is shown below:
Types

A ::= Int | A1 → A2 | A1 & A2

Terms E ::= i | x | λx. E | E1 E2 | E1 , , E2 | E : A
Its full (bidirectional) type system is shown in Fig. 2.1. (It is probably best to skim this figure
on first reading. We will explain and contrast this figure in later chapters.) Central to theirsystem is the notion of disjointness. As a first approximation, for two types A and B to be
disjoint (written A ∗ B), they must not have any sub-components sharing the same type. Ina type system without ⊤, this can be ensured by the following specification:
Definition 1 (Simple disjointness). A ∗ B ≜ ∄C. A <: C ∧ B <: C
The disjointness judgment appears in the well-formedness of intersection types (rule wfand) and the typing rule of merges (rule Ti-merge). Rule wf-and—the well-formedness ofintersections—enforces that only disjoint types can form an intersection type: so Int & Boolis well-formed but Int & Int is not. Rule Ti-merge—the typing rule for merges—prevents
problematic merges such as 1 , , 2 (because Int and Int are not disjoint), while accepting
unambiguous merges such as 1 , , true.
Remark. Note that the introduction form for disjoint intersection types (rule Ti-merge) is
not as expressive as rule interI. For instance, rule interI entails the following derivation:
λx. x : Int → Int

λx. x : Char → Charλx.x : (Int → Int) & (Char → Char)which is impossible to express in λi .
To ensure that subtyping produces unique coercions, λi employs the notion of ordinary
types [Davies and Pfenning 2000]—those that are not intersection types—and use the judgment “A ordinary” in rules Si-andL and Si-andR. Ordinary types and disjointness are sufficient to ensure a coherent semantics of a type system without ⊤.

20

2.1 Intersection Types

A <: B ⇝ e(Subtyping)Si-intSi-top

Int <: Int ⇝ λx. x

A <: ⊤ ⇝ λx. ⟨⟩Si-arr
B1 <: A1 ⇝ e1
A2 <: B2 ⇝ e2
A1 → A2 <: B1 → B2 ⇝ λf. λx. e2 (f (e1 x))

Si-andA1 <: A2 ⇝ e1
A1 <: A3 ⇝ e2
A1 <: A2 & A3 ⇝ λx. ⟨e1 x, e2 x⟩

Si-andL
A1 <: A3 ⇝ e
A3 ordinary
A1 & A2 <: A3 ⇝ λx. JA3 Ke (π1 x)Si-andR
A2 <: A3 ⇝ e
A3 ordinary
A1 & A2 <: A3 ⇝ λx. JA3 Ke (π2 x)Γ⊢A(Well-formedness of types)

wf-intwf-topΓ ⊢ Int

Γ⊢⊤

wf-andΓ⊢AΓ⊢BΓ ⊢ A&B

A∗B

wf-arr
Γ⊢A
Γ⊢B
Γ⊢A→B

Γ⊢E⇒A

(Inference)

Ti-topTi-litΓ ⊢ ⊤ ⇒ ⊤ ⇝ ⟨⟩

Γ ⊢ i ⇒ Int ⇝ i

Ti-appΓ ⊢ E1 ⇒ A1 → A2 ⇝ e1
Γ ⊢ E2 ⇐ A1 ⇝ e2
Γ ⊢ E1 E2 ⇒ A2 ⇝ e1 e2

Ti-var(x : A) ∈ ΓΓ⊢x ⇒ A⇝x
Ti-annoΓ⊢E ⇐ A⇝eΓ⊢E:A ⇒ A⇝e

Ti-mergeΓ ⊢ E1 ⇒ A1 ⇝ e1
Γ ⊢ E2 ⇒ A2 ⇝ e2
A1 ∗ A2
Γ ⊢ E1 , , E2 ⇒ A1 & A2 ⇝ ⟨e1 , e2 ⟩
Γ⊢E⇐A

(Checking)Ti-absΓ⊢AΓ, x : A ⊢ E ⇐ B ⇝ eΓ ⊢ λx. E ⇐ A → B ⇝ λx. e

Ti-subΓ⊢BΓ⊢E ⇒ A⇝e
A <: B ⇝ e′Γ ⊢ E ⇐ B ⇝ e′ eFigure 2.1: Type system of λi

21

2 Background⊤ brings extra complications, because Definition 1 does not hold anymore (⊤ is trivially asupertype of every type). To address this problem, the notion of top-like types was introduced,
which are those types that behave like ⊤ (such as ⊤ & ⊤, ⊤ & ⊤ & ⊤, . . . ), and captured by apredicate ⌉ · ⌈. An important observation is that any coercions for top-like types are unique,even if multiple derivations exist. The meta-function J·K used in rules Si-andL and Si-andR
defines coercions for top-like types. With top-like types, Definition 1 is refined to accountfor ⊤, as shown in Definition 2.
Definition 2 (⊤-Disjointness). A ∗ B ≜ ¬⌉A⌈ ∧ ¬⌉B⌈ ∧ (∀C. A <: C ∧ B <: C =⇒⌉C⌈)
However, a careful analysis of Definition 2 shows that intersection types such as ⊤ & ⊤and ⊤ & Int are not well-formed because their constituent types are not disjoint. This is oneof the limitations in λi , since “a merge of two ⊤-types will always return the same value
regardless of which component of the merge is chosen” [Alpuim et al. 2017]. In other words,⊤ is always disjoint to every other type. This restriction was later lifted in the Fi calculus of
Alpuim et al. [2017] by a set of inference rules, but whether a corresponding specificationof disjointness exists or not was not known at that time. A suitable specification will be
proposed in Section 10.1.
Combined with bidirectional type-checking, Oliveira et al. [2016] formalized the λi calculus in Coq and prove that there is at most one elaboration derivation for any expression,and as a consequence, there is only one possible target program and thus coherence follows
trivially. We refer the reader to their paper for a detailed account of λi .

.. Disjoint Polymorphism
Disjoint polymorphism, first proposed by Alpuim et al. [2017] in the Fi calculus, is a moreadvanced mechanism to combine disjoint intersection types with parametric polymorphism.
The combination allows objects with statically unknown types to be composed without conflicts. To understand the usefulness of disjointness polymorphism, consider the following
program (adapted from Alpuim et al. [2017]):
mergeBad X (x : X) : X & Int = x ,, 2;
mergeBad takes an argument x of type X (which is itself a type variable), and merges it with
2. However, if we were to allow such definition, we could easily create an example where

incoherence occurs again:(mergeBad Int 1) : Int -- 1 or 2

This is essentially the same problem of allowing 1 , , 2, which as we discussed will cause ambiguity. For λi , we know the concrete type for each variable and thus disjointness checking can

22

2.2 Mixins and Traits
help avoid this problematic expression. However, with parametric polymorphism, a variable
could have any types, including those that are already in the intersection. So a question to ask
is to decide under what conditions a type variable is disjoint with, say, Int. This is where disjointness constraints come into stage. The key idea is to restrict the set of types a type variable
can be instantiated to. Let us rewrite the above program as follows:
mergeGood [X * Int] (x : X) : X & Int = x ,, 2;

The only change is the notation [X * Int], where the left-side of * denotes the type variablebeing declared, and the right-side denotes the disjointness constraint(s). Here the disjointness constraint (Int) effectively states that the type variable X can be instantiated to any types
disjoint with Int. For instance, the expression mergeGood Bool True type checks but theexpression mergeGood Int 1 is rejected because Int (the type argument) is not disjoint with
Int (the disjointness constraint). Moreover, we can express multiple constraints using inter-

section types, for example,
mergeThree [X * Int & Bool] (x : X) : X & Int & Bool = x ,, 2 ,, True;

Here the type variable X can only be instantiated to types disjoint with both Int and Bool.
In essence disjoint intersection types and disjoint polymorphism retain most of the expressive power of the merge operator. For example, as noted by Alpuim et al. [2017], they
can be used to model powerful forms of extensible records. However, forcing every intersection types to be disjoint is unnecessarily restrictive. For instance, 1 : Int & Int is undoubtedly
unambiguous, but is rejected by λi and Fi . Another issue is that because of the restriction, Fi
lacks a general substitution lemma; only a restricted form applies, which greatly complicatesthe metatheory. Our starting point in this thesis is to lift this restriction and makes room for
more expressiveness for calculi with disjoint intersection types.

. Mixins and Traits
Programmers have long realized that single inheritance is not flexible enough when it comesto structuring a class hierarchy. For example, consider two classes in different branches of theinheritance hierarchy, and assume that they share features not inherited from their (unique)common parent. Attempting to share the implementation of the common features may leadto putting the common methods too high in the hierarchy (i.e., they are forced into theircommon parent), and these methods will be inherited by other classes in the same hierarchy,
which may not be desirable. On the other hand, putting those methods in a lower position
results in code duplication. To overcome this limitation, multiple inheritance was proposed
as a generalization of single inheritance. However, as Steve Cook [Cook 1987] put it:23

2 BackgroundA
method mAmethod mBoverride mCoverride m

D
m?

B

M1
method m

M2
method mC(a) The diamond problem(b) Mixin composition

Figure 2.2: Multiple inheritance and mixins“Multiple inheritance is good, but there is no good way to do it.”
One of the problems in multiple inheritance is the ambiguity issue that arises when conflicting features are inherited along different paths. A classic situation is the diamond problem [Bracha and Cook 1990] where a class inherits from two parent classes that have a common superclass, as depicted by Fig. 2.2a.
Mixins and traits are two well-studied mechanisms to provide some form of multiple inheritance. Mixins [Bracha and Cook 1990] provide a simple mechanism for multiple inheritance without the ambiguity issue. A mixin is a subclass declaration parameterized over asuperclass. Or simply put, a mixin can be treated as a function from classes to classes. Thus
the same mixin can be used to extend a variety of parent classes with the same set of features.
Figure 2.2b shows a typical class hierarchy when using mixins. In the mixin model, a class
can inherit from another class by means of single inheritance as usual. Apart from that, it canalso have several mixins applied one at a time. Let us take a close look at Fig. 2.2b. Both mixins M1 and M2 contain a method m, a question arises as to which one is inherited in the class
C. The answer is m from the mixin M2. This is because mixin composition is linear: methods

defined in mixins appearing later override all the identically named methods of earlier mixins. While this simple mechanism does avoid conflicts, it also lead to other problems. For
example, though we can obtain the method m from the mixin M1 by switching the order of M1
and M2, no suitable order of composition exists to obtain m from the superclass A.
In response to the problems in the then compositional models, Schärli et al. [2003] proposed a mechanism called traits as a better way to foster code reuse in object-oriented programs. A trait is essentially a set of pure methods, divorced from any class hierarchy. A trait
provides a set of methods to implement the behavior, and it may also specify a set of required24

2.3 Family Polymorphism and Nested Composition

CircleradiusTCirclehasharearadiusTDraw

TCircleProvided methods

hash
area

radiushashdraw

Requirements(a) A simple trait

area(b) Trait composition with conflicts
Figure 2.3: Traits and conflicts

methods that parameterize the provided behavior. Figure 2.3a shows a simple trait TCircle,
which provides two methods hash and area, and requires a method radius. A class is thenconstructed by inheriting from a superclass and incorporating a collection of traits, as shownin Fig. 2.3b. Notice that there is a conflicting method hash that is provided by both TCircle
and TDraw. This is where the trait model is very different from the mixin model. Unlike mixins that force a linear order in their composition, traits can be composed in arbitrary order,
and as a consequence, conflicting methods must be resolved explicitly, either by overriding
the conflicting methods, or by excluding a method from all but one trait. Schärli et al. [2003]
discuss several other issues with mixins, which can be improved by traits. We refer to theirpaper for a detailed account of traits.

. Family Polymorphism and Nested Composition
Family polymorphism [Ernst 2001] is the ability to simultaneously refine a family of relatedclasses through inheritance. This is motivated by a need to not only refine individual classes,
but also to preserve and refine their mutual relationships. Nystrom et al. [2004] call this scalable extensibility: “the ability to extend a body of code while writing new code proportional
to the differences in functionality”. A well-studied mechanism to achieve family inheritanceis nested inheritance [Nystrom et al. 2004]. Nested inheritance combines two aspects. Firstly,a class can have nested class members; the outer class is then a family of (inner) classes. Secondly, when one family extends another, it inherits (and can override) all the class members,
as well as the relationships within the family between the class members. However, the members of the new family do not become subtypes of those in the parent family.

25

2 BackgroundThe expression problem. Ernst [2004] illustrates the benefits of nested inheritance formodularity and extensibility with one of the most elegant and concise solutions to the expression problem [Wadler 1998]. The expression problem, as surveyed by Torgersen [2004],
is to answer the question:“To which degree can your application be structured in such a way that both thedata model and the set of virtual operations over it can be extended without theneed to modify existing code, without the need for code repetition and without
run-time type errors.”
The expression problem is concerned with two-dimensional extensions: (1) adding new variants to the datatype; (2) and adding new operations on the datatype. Depending on the
programming style used in the code, it is usually straightforward to add either new variants
or new operations. For example, in an object-oriented language such as Java where an abstract datatype is represented by means of classes whose methods are the operations on thedatatype, it is easy to extend the set of variants by writing another class. On the other hand,in a functional language such as Haskell where the abstract datatype is modeled by means ofalgebraic datatypes with a set of pattern matching functions as the operations, then it is easy
to add new operations by writing new pattern matching functions. In either case, it is muchharder to perform both extensions in the same language.
The expression problem, Scandinavian style.

Nowadays we know many solutions to

the expression problem (for example, see Oliveira [2009]; Oliveira and Cook [2012]; Swierstra [2008]; Wang and Oliveira [2016]; Zenger and Odersky [2005], to cite a few). Among
all of them, Ernst’s solution is perhaps one of the most elegant solutions out there. Ernst
solves the expression problem in the gbeta language [Ernst 2000], which he adorns with aJava-like syntax for presentation purposes, for a small abstract syntax tree (AST) example.
His starting point is the code shown in Fig. 2.4a. The outer class Lang contains a family of
related AST classes: the common superclass Exp and two classes, Lit for literals and Add for
addition. The AST comes equipped with one operation, toString, which is implemented byboth cases. Notice that all the inner classes are virtual, in the same sense of virtual methods,
which means that they may be redefined in subclasses of the enclosing class.
Adding a new operation. One way to extend the family is to add an additional evaluation operation, as shown in the top half of Fig. 2.4b. This is done by subclassing the Langclass and refining all the contained classes by implementing the additional eval method.
The semantics of the keyword refine is that the virtual class is constrained to be a subclass26

2.3 Family Polymorphism and Nested Composition

class Lang {virtual class Exp {String toString() {}}virtual class Lit extends Exp {int value;Lit(int value) {this.value = value;}
String toString() {return value;}}
virtual class Add extends Exp {Exp left,right;
Add(Exp left, Exp right) {this.left = left;
this.right = right;}
String toString() {return left + "+" + right;}}}// Adding a new operation
class LangEval extends Lang {refine class Exp {int eval() {}}refine class Lit {int eval { return value; }}refine class Add {int eval { returnleft.eval() + right.eval();}}}// Adding a new caseclass LangNeg extends Lang {virtual class Neg extends Exp {Neg(Exp exp) { this.exp = exp; }String toString() {return "-(" + exp + ")";}Exp exp;}}(a) Base family: the language Lang(b) Extending in two dimensions

Figure 2.4: The expression problem, Scandinavian styleof the new declaration. In other words, Exp, Lit and Add are all extended with the evalmethod. Note that the inheritance between, e.g., Lang.Exp and Lang.Lit is transferred to
LangEval.Exp and LangEval.Lit. Similarly, the Lang.Exp type of the left and right fields

in Lang.Add is automatically refined to LangEval.Exp in LangEval.Add.
Adding a new case. A second dimension to extend the family is to add a case for negation,
shown in the bottom half of Fig. 2.4b. This is similarly achieved by subclassing Lang, and now
adding a new contained virtual class Neg that represents the unary negation operator. Notethat Neg is declared to be a subclass of Exp, which means that the extension to Exp will also
be added to Neg.
Combining both extensions. Finally, the two extensions are naturally combined by meansof multiple inheritance, closing the diamond. (Ernst uses the symbol ⊕ to play the role of“intersecting” two classes.)27

2 Backgroundclass LangNegEval extends LangEval ⊕ LangNeg {refine class Neg {int eval() { return -exp.eval(); }}}The only effort required is to implement the one missing operation case, evaluation of negatedexpressions.

. Functional Object Encodings
Cook and Palsberg [1989] developed a method for modeling inheritance in the presence ofself-reference, based on the fixed-point semantics of recursive definitions. In their model,
the interpretation of inheritance is taken as a mechanism of incremental programming, where
new programs are developed by specifying the modification—i.e., how they differ from existing ones; self-reference in the original definition must be changed to refer to the modified
definition.
We use an example to illustrate the encodings of classes and objects. First we define a classof points. Points have two components x and y to specify their locations. The dist method
computes their (Euclidean) distance from the origin. The following is a Scala class Point:class Point(x : Int, y : Int) {def dist() = sqrt(square(this.x) + square(this.y))}

In a purely functional setting, objects are modeled as records whose fields represent methods. The class Point is then modeled as a generator PointGen(a, b), defined as follows:
PointGen(a, b) = λthis.
{ x = a, y = b, dist() = sqrt(square(this.x) + square(this.y))}

Notice that the keyword this is modeled as a formal parameter of the function. Formallyspeaking, a function intended to specify a fixed point whose formal parameter represents
self-reference is called a generator.
A point (3, 4) is created by taking a fixed point of PointGen(3, 4) using a lazy recursive
let binding:p = letrec this = PointGen(3, 4, this) in this28

2.4 Functional Object Encodings
and method invocation on objects is simply record projections: p.dist() evaluates to 5 as
expected.

Modeling inheritance. Inheritance allows a new class to be defined by adding or replacing methods in an existing class. We illustrate this by defining another class Circle:
class Circle(x : Int, y : Int, radius : Int) extends Point(x, y) {override def dist() = abs(super.dist() - this.radius)}

The class Circle inherits from Point and redefines the method dist to mean the closest
distance from the circle to the origin. It reuses the original dist method in the body. To
correctly model inheritance, there are three aspects to note: (1) the addition or replacementof methods, (2) the redirection of this in the original generator to the modified methods,(3) and the binding of super to refer to the original methods.
Inheritance is modeled as a function that takes a generator and returns a new generator.
Such functions are called wrappers. Below we give a wrapper for the subclass Circle:
CircleWrapper(a, b, r) = λthis. λsuper.
{ radius = r, dist() = abs(super.dist() - this.radius)}
CircleWrapper(a, b, r) is defined as a function of two arguments, one representing thisand the other representing super.
The generator for the class Circle can now be defined by applying CircleWrapper toPointGen as follows:
CircleGen(a, b, r) = λthis.
let super = PointGen(a, b, this)in (CircleWrapper(a, b, r, this) super) ⊕ superThat is, a wrapper works by first distributing this to both the wrapper and the original generator. Then the modification is applied to the original record definition to produce a modification record. Note that at this stage, the binding of this correctly refers to the modification,
while the binding of super refers to the original record. Finally the modification record iscombined with the original record using ⊕. (M ⊕ N is defined in a way such that any method
defined in M replaces the corresponding method defined in N.)

29

2 Background. Program Equivalence and Logical Relations
Proving equivalence of programs is important for a variety of settings, e.g., verifying thecorrectness of compiler optimization and other program transformations, establishing theproperty that program behavior is independent of the representation of an abstract type.
The latter—so-called the property of representation independence—is particularly relevant
for programmers and clients in the sense that a client will not be able to tell a difference if
one implementation is swapped by another, as long as they all adhere to the same interface.
Program equivalence is generally defined in terms of contextual equivalence. The intuitionis that two programs are equivalent if we cannot tell them apart in any context. More formally,
we introduce the notion of expression contexts. An expression context D is a term with asingle hole [·] (possibly under some binders) in it. Take the simply-typed lambda calculus(STLC) for example, the syntax of expression contexts is as follows:
Contexts D ::= [·] | λx. D | D e | e D
The only operation of expression contexts is replacement, which is the process of filling ahole in an expression context D with an expression e, written D{e}. An important point isthat replacement is not substitution, that is, the free variables of e that are exposed by D are
captured by replacement. The static semantics of STLC is extended to expression contexts bydefining the typing judgment
D : (Ψ ⊢ τ ) 7→ (Ψ′ ⊢ τ ′ )where (Ψ ⊢ τ ) indicates the type of the hole. This judgment is inductively defined so that ifΨ ⊢ e : τ , then Ψ′ ⊢ D{e} : τ ′ .
Contextual equivalence. We define a complete program to mean any closed term oftype Int. The following two definitions capture the notion of contextual equivalence:
Definition 3 (Kleene Equality ⋍). Two complete programs, e and e′ , are Kleene equal, written e ⋍ e′ , if there exists i such that e −→∗ i and e′ −→∗ i.
Definition 4 (Contextual Equivalence ⋍ctx ).
Ψ ⊢ e1 ⋍ctx e2 : τ ≜ Ψ ⊢ e1 : τ ∧ Ψ ⊢ e2 : τ ∧(∀D. D : (Ψ ⊢ τ ) 7→ (• ⊢ Int) =⇒ D{e1 } ⋍ D{e2 })30

2.5 Program Equivalence and Logical Relations
In other words, for all possible experiments D, the outcome of an experiment on e1 is thesame as the outcome on e2 (i.e., D{e1 } ⋍ D{e2 }), which is an equivalence relation.
Logical relations. Unfortunately, directly proving contextual equivalence is very difficult in general (if not possible at all), since it involves quantification over all possible contexts.
There has been much work on finding tractable techniques for proving contextual equivalence, many of which are based on the proof method called logical relations [Plotkin 1973;
Statman 1985; Tait 1967].
In a nutshell, logical relations specify relations over well-typed terms via a structural induction on the syntax of types. For instance, logically related functions, when taken logicallyrelated arguments, return logically related results. For STLC, the logical relation is a familyof relations (v1 , v2 ) ∈ VJτ K between closed values of type τ . It is inductively defined on the
structure of τ as follows:(v1 , v2 ) ∈ VJIntK ≜ ∃i. v1 = v2 = i(v1 , v2 ) ∈ VJτ1 → τ2 K ≜ ∀(v′1 , v′2 ) ∈ VJτ1 K. (v1 v′1 , v2 v′2 ) ∈ EJτ2 K(e1 , e2 ) ∈ EJτ K ≜ ∃v1 , v2 . e1 −→∗ v1 ∧ e2 −→∗ v2 ∧ (v1 , v2 ) ∈ VJτ K
That is, two integers are related if they are the same integer. Two functions v1 and v2 are
related at the type τ1 → τ2 if given two arguments v′1 and v′2 related at the domain type τ1 ,the functions applied to the arguments are related expressions at the codomain type τ2 .
Logical and contextual equivalence coincide. The usefulness of the logical relation
lies in the fact that it characterize exactly contextual equivalence—i.e., logical and contextual
equivalence coincide for STLC:
Proposition 2.1. For closed expression e : τ and e′ : τ , (e, e′ ) ∈ EJτ K iff • ⊢ e ⋍ctx e′ : τ .
The proofs proceeds by generalizing to open terms, which will be explained in more details
in Chapter 5. The above proposition licenses a common approach of proving properties
involving contextual equivalence: we first prove a related property using logical relations,and then transfer it back to the one involving contextual equivalence.

31

Part II
Typed Calculi

33 Semantics of the λ Calculus+i1This chapter presents λ+i , a calculus based on λi [Oliveira et al. 2016] that features unre-stricted intersections, BCD-style subtyping and a merge operator, which we believe captures
the essence of nested composition. We illustrate this by presenting a solution to the expression problem based on family polymorphism. We then discuss the algorithmic aspects of+
λ+i . The coherence property of λi is discussed in Chapter 5.

. Introduction
λ+i is a simple calculus with records and disjoint intersection types that supports nested composition. Nested composition enables encoding simple forms of family polymorphism. Morecomplex forms of family polymorphism, involving binary methods [Bruce et al. 1996] andmutable state are not yet supported, but are interesting avenues for future work. Nevertheless, in λ+i , it is possible, for example, to encode Ernst’s elegant family-polymorphism solution to the expression problem. Compared to λi the essential novelty of λ+i are distributivity
rules between function/record types and intersection types. These rules are the delta thatenables extending the simple forms of multiple inheritance/composition supported by λi
into a more powerful form supporting nested composition. The distributivity rule between
function types and intersections is common in calculi with intersection types aimed at capturing the set of all strongly normalizable terms, and was first proposed by Barendregt et al.
[1983]. However the distributivity rule is not common in calculi or languages with intersection types aimed at programming. For example the rules employed in languages that support intersection types (such as Scala, TypeScript, Flow or Ceylon) lack distributivity rules.
Moreover distributivity is also missing from several calculi with a merge operator. This includes all calculi with disjoint intersection types [Alpuim et al. 2017; Oliveira et al. 2016]
and Dunfield’s work on elaborating intersection types [Dunfield 2014], which was the original foundation for λi . A possible reason for this omission in the past is that distributivity
adds substantial complexity (both algorithmically and meta-theoretically), without having
1

It was also called NeColus in the original publication [Bi et al. 2018]. Also the “+” symbol stands for two extra
features compared to λi : BCD subtyping and unrestricted intersections.

35

3 Semantics of the λ+i Calculus
any obvious practical applications. This chapter shows how to deal with the complicationsof BCD subtyping, while identifying a major reason to include it in a programming language:
BCD enables nested composition and subtyping, which is of significant practical interest.
λ+i differs significantly from previous BCD-based calculi in that it has to deal with thepossibility of incoherence, introduced by the merge operator. Incoherence is a non-issue inthe previous BCD-based calculi because they do not feature this merge operator or any othersource of incoherence. Although previous work on disjoint intersection types proposes asolution to coherence, the solution imposes several ad-hoc restrictions (cf. Section 3.5) toguarantee the uniqueness of the elaboration and thus allows for a simple syntactic proof of
coherence. Most importantly, it makes it hard or impossible to adapt the proof to extensionsof the calculus, such as the new subtyping rules required by the BCD system. We shall returnto this point in Chapter 5.

. λ+
i by Examples
This section illustrates λ+i with an encoding of a family polymorphism solution to the expression problem, and informally presents its salient features.

.. The Expression Problem, λ+i StyleThe λ+
i calculus allows us to solve the expression problem in a way that is very similar to
Ernst’s gbeta solution in Section 2.3. However, the underlying mechanisms of λ+i are quite
different from those of gbeta. In particular, λ+i features a structural type system in which we
can model objects with records, and object types with record types. For instance, we model
the interface of Lang.Exp with the singleton record type { print : String }. For the sakeof conciseness, we use type aliases to abbreviate types.
type IPrint = { print : String };Similarly, we capture the interface of the Lang family in a record, with one field for each case’s
constructor.
type Lang = {lit : Int → IPrint,
add : IPrint → IPrint → IPrint};

Here is the implementation of Lang.
implLang : Lang = {lit (value : Int) = {36

3.2 λ+i by Examples
print = value.toString},add (left : IPrint) (right : IPrint) = {print = left.print ++ "+" ++ right.print}};

We assume several primitive types: fixed width integers Int, Double for numeric operations
and String for text manipulation. A λ+i program consists of a collection of definitions anddeclarations, separated by semicolon ;.
Adding evaluation. We obtain IPrint & IEval, which is the corresponding type for
LangEval.Exp, by intersecting IPrint with IEval wheretype IEval = { eval : Int };The type for LangEval is thentype LangEval = {
lit : Int → IPrint & IEval,
add : IPrint & IEval → IPrint & IEval → IPrint & IEval};

We obtain an implementation for LangEval by merging the existing Lang implementation
implLang with the new evaluation functionality implEval using the merge operator ,,.
implEval = {lit (value : Int) = {eval = value},
add (left : IEval) (right : IEval) = {eval = left.eval + right.eval}};
implLangEval : LangEval = implLang ,, implEval;

Adding negation. Adding negation to Lang works similarly.
type NegPrint = { neg : IPrint → IPrint };type LangNeg = Lang & NegPrint;
implNegPrint : NegPrint = {neg (exp : IPrint) = {37

3 Semantics of the λ+i Calculus
print = "-" ++ exp.print}};
implLangNeg : LangNeg = implLang ,, implNegPrint;

Putting everything together. Finally, we can combine the two extensions and provide
the missing implementation of evaluation for the negation case.
type NegEval = { neg : IEval → IEval};
implNegEval : NegEval = {neg (exp : IEval) = {eval = 0 - exp.eval}};
type NegEvalExt = { neg : IPrint & IEval → IPrint & IEval };type LangNegEval = LangEval & NegEvalExt;
implLangNegEval : LangNegEval =implLangEval ,, implNegPrint ,, implNegEval;

We can test implLangNegEval by creating an object that represents −2 + 3, which is able toprint and evaluate at the same time.
fac = implLangNegEval;e= fac.add (fac.neg (fac.lit 2)) (fac.lit 3);
main = e.print ++ " = " ++ e.eval.toString -- Output: "-2+3 = 1"

Multi-field records. Recall that in Section 2.1, we show how to model multi-field records
by single-field records. Thus λ+i does not have multi-field record types built in. They are
merely syntactic sugar for intersections of single-field record types. Hence, the following is
an equivalent definition of Lang:
type Lang = {lit : Int → IPrint} & {add : IPrint → IPrint → IPrint};

Similarly, the multi-field record expression in the definition of implLang is syntactic sugarfor the explicit merge of two single-field records.
implLang : Lang = { lit = ... } ,, { add = ... };Subtyping. A distinctive difference compared to gbeta is that many more λ+i types are
related through subtyping. Indeed, gbeta is unnecessarily conservative [Ernst 2003]: none38

3.2 λ+i by Examplesof the families is related through subtyping, nor is any of the class members of one family
related to any of the class members in another family. For instance, LangEval is not a subtypeof Lang, nor is LangNeg.Lit a subtype of Lang.Lit.
In contrast, subtyping in λ+i is much more nuanced and depends entirely on the structureof types. The primary source of subtyping are intersection types: any intersection type is asubtype of its components. For instance, IPrint & IEval is a subtype of both IPrint andIEval. Similarly LangNeg = Lang & NegPrint is a subtype of Lang. Compare this to gbeta

where LangEval.Expr is not a subtype of Lang.Expr, nor is the family LangNeg a subtype ofthe family Lang.
+
However, gbeta and λ+i agree that LangEval is not a subtype of Lang. The λi -side of thismay seem contradictory at first, as we have seen that intersection types arise from the use of
the merge operator. We have created an implementation for LangEval with implLang ,,implEval where implLang has type Lang, which suggests that LangEval is a subtype of Lang.

Yet, there is a flaw in our reasoning: strictly speaking, implLang ,, implEval is not of type
LangEval but instead of type Lang & EvalExt, where EvalExt is the type of implEval:
type EvalExt = { lit : Int → IEval, add : IEval → IEval → IEval };Nevertheless, the definition of implLangEval is valid because Lang & EvalExt is a subtypeof LangEval. Indeed, if we consider for the sake of simplicity only the lit field, we have that(Int → IPrint) & (Int → IEval) is a subtype of Int → IPrint & IEval. This follows

from a standard subtyping axiom for distributivity of functions and intersections in the BCD
system inherited by λ+i . In conclusion, Lang & EvalExt is a subtype of both Lang and of
LangEval. However, neither of the latter two types is a subtype of the other. Indeed, LangEval

is not a subtype of Lang as the type of add is not covariantly refined and thus admitting thesubtyping is unsound. For the same reason Lang is not a subtype of LangEval.
A summary of the various relationships between the language components is shown in
Fig. 3.1. Admittedly, the figure looks quite complex because our calculus has a structural
type system (as often more foundational calculi do) where more types are related throughsubtyping, whereas mainstream object-oriented languages have nominal type systems.

Stand-alone extensions. Unlike in gbeta and other class-based inheritance systems, in
λ+i the extension implEval is not tied to LangEval. In that sense, it resembles trait and mixin
systems that can apply the same extension to different classes. However, unlike those systems,
implEval can also exist as a value on its own, i.e., it is not an extension per se.

39

3 Semantics of the λ+i Calculus

NegEvalExt

LangEval

LangNegEval

NegEvalNegPrint

NegEval & NegPrint

implNegEval,,
implNegPrint

implNegEval

Lang

EvalExt

LangNeg

implNegPrint

Lang & EvalExt

implLangNeg

implLang

implLangEval

implEval

implLangNegEval
LEGEND:#Interface##composition

implmentation

subtype-of

impl-ofFigure 3.1: Summary of the relationships between language components

. Syntax and Semantics of λ+i
In this section we formally present the syntax and semantics of λ+i . Compared to previous
work [Alpuim et al. 2017; Oliveira et al. 2016], λ+i has a more powerful subtyping relation.
The new subtyping relation is inspired by BCD subtyping, but with two noteworthy differences: subtyping is coercive (in contrast to traditional formulations of BCD); and it is extended with records. We also have a new target language with explicit coercions inspired by
the coercion calculus of Henglein [1994]. A full technical comparison between λ+i and λican be found in Section 3.5.

.. Syntax
Figure 3.2 shows the syntax of λ+i . For brevity of the meta-theoretic study, we do not consider primitive operations on primitive types. They can be easily added to the language,
and our prototype implementation is indeed equipped with common primitive types andtheir operations. Metavariables A, B, C range over types. Types include integer types Int, a
top type ⊤, function types A → B, intersection types A & B, and single-field record types{l : A}. Metavariable E ranges over expressions. Expressions include variables x, literals i,a canonical top value ⊤, lambda abstractions λx. E, applications E1 E2 , merges E1 , , E2 ,annotated terms E : A, single-field records {l = E}, and record projections E.l.

40

3.3 Syntax and Semantics of λ+i
TypesExpressions
Term contexts

A, B, C
E
Γ

::=
::=
::=

Int | ⊤ | A → B | A & B | {l : A}x | i | ⊤ | λx. E | E1 E2 | E1 , , E2 | E : A | {l = E} | E.l• | Γ, x : A
Figure 3.2: Syntax of λ+i

.. Declarative Subtyping
Figure 3.3 presents the subtyping relation. We ignore the highlighted parts, and explainthem later in Section 3.4.
BCD-style subtyping. The subtyping relation is essentially BCD subtyping [Barendregt
et al. 1983], extended with subtyping for single-field records. The top type is a supertype of all
types (rule S-top). Rules S-andL, S-andR, and S-and for intersection types axiomatize thatA & B is the greatest lower bound of A and B. Rules S-arr and S-rcd for function and record
subtyping are standard. Rule S-distArr is perhaps the most interesting rule. This, so-called“distributivity” rule, describes the interaction between the subtyping relations for function
types and those for intersection types. Note that the other direction A1 → A2 & A3 <:(A1 → A2 ) & (A1 → A3 ) and the contravariant distribution (A1 → A2 ) & (A3 → A2 ) <:A1 & A3 → A2 are both derivable from the existing subtyping rules, as shown below:
A1 <: A1A2 & A3 <: A2

A1 → A2 & A3 <: A1 → A2

S-arr

A1 <: A1A2 & A3 <: A3

A1 → A2 & A3 → A1 → A3

S-arr

A1 → A2 & A3 <: (A1 → A2 ) & (A1 → A3 )(A1 → A2 ) & (A3 → A2 ) <: A1 → A2

S-andLA1 & A3 <: A1

A2 <: A2

A1 → A2 <: A1 & A3 → A2

(A1 → A2 ) & (A3 → A2 ) <: A1 & A3 → A2

S-and

S-arr
S-trans

Rule S-distRcd, which is not found in the original BCD system, prescribes the distributionof records over intersection types. The two distributivity rules are the key to enabling nested
composition. S-topArr is standard in BCD subtyping, and the new rule S-topRcd plays asimilar role for record types.
Non-algorithmic. The subtyping relation in Fig. 3.3 is clearly no more than a specification due to the two subtyping axioms: rules S-refl and S-trans. A sound and complete
algorithmic version is discussed in Section 3.6. Nevertheless, for the sake of establishingcoherence, the declarative subtyping relation is sufficient.

41

3 Semantics of the λ+i Calculus
A <: B ⇝ co

(Declarative subtyping)
S-trans
A2 <: A3 ⇝ co1

S-refl

A1 <: A2 ⇝ co2

A <: ⊤ ⇝ top

A1 <: A3 ⇝ co1 ◦ co2

A <: A ⇝ id
S-andL

S-andR

A1 & A2 <: A1 ⇝ π1

A1 & A2 <: A2 ⇝ π2

S-arr
B1 <: A1 ⇝ co1

A2 <: B2 ⇝ co2

A1 → A2 <: B1 → B2 ⇝ co1 → co2

S-top

S-and
A1 <: A2 ⇝ co1

A1 <: A3 ⇝ co2

A1 <: A2 & A3 ⇝ ⟨co1 , co2 ⟩S-rcd
A <: B ⇝ co{l : A} <: {l : B} ⇝ coS-distArr

S-distRcd(A1 → A2 ) & (A1 → A3 ) <: A1 → A2 & A3 ⇝ dist→{l : A} & {l : B} <: {l : A & B} ⇝ id

S-topArr

S-topRcd⊤ <: ⊤ → ⊤ ⇝ top→

⊤ <: {l : ⊤} ⇝ id

Figure 3.3: Declarative subtyping of λ+iProperties of subtyping.

The subtyping relation is vacuously reflexive and transitive.

.. Typing of λ+i
The bidirectional type system for λ+i is shown in Fig. 3.4. We ignore the highlighted partsfor now.
Typing rules. The typing rules of λ+i in Fig. 3.4 are mostly ported from λi in Fig. 2.1. Aswith traditional bidirectional type systems, two modes are employed: the inference mode(⇒) and the checking mode (⇐). The inference judgment Γ ⊢ E ⇒ A says that we cansynthesize a type A for expression E in the context Γ. The checking judgment Γ ⊢ E ⇐ A
checks E against A in the context Γ. Most of the rules are quite standard in the literature.
The merge expression E1 , , E2 is well-typed if both sub-expressions are well-typed, and theirtypes are disjoint (rule T-merge).
Disjointness rules. The set of inference rules for disjointness A ∗ B is shown in Fig. 3.5.
Note that our set of disjointness rules is different from that in λi [Oliveira et al. 2016, Figure 10]: λi does not have rules D-topL and D-topR, which first appeared in Fi [Alpuim et al.

42

3.4 Syntax and Semantics of λcoΓ⊢E⇒A⇝e(Inference)

T-top

T-lit

Γ ⊢ ⊤ ⇒ ⊤ ⇝ ⟨⟩

Γ ⊢ i ⇒ Int ⇝ iT-appΓ ⊢ E1 ⇒ A1 → A2 ⇝ e1

Γ ⊢ E2 ⇐ A1 ⇝ e2

Γ ⊢ E1 E2 ⇒ A2 ⇝ e1 e2

T-projΓ ⊢ E ⇒ {l : A} ⇝ eΓ ⊢ E.l ⇒ A ⇝ e

T-var(x : A) ∈ ΓΓ⊢x⇒A⇝xT-annoΓ⊢E⇐A⇝eΓ⊢E:A⇒A⇝e

T-mergeΓ ⊢ E1 ⇒ A1 ⇝ e1
Γ ⊢ E2 ⇒ A2 ⇝ e2
A1 ∗ A2
Γ ⊢ E1 , , E2 ⇒ A1 & A2 ⇝ ⟨e1 , e2 ⟩T-rcd
Γ⊢E⇒A⇝eΓ ⊢ {l = E} ⇒ {l : A} ⇝ eΓ⊢E⇐A⇝e(Checking)T-abs
Γ, x : A ⊢ E ⇐ B ⇝ eΓ ⊢ λx. E ⇐ A → B ⇝ λx. e

T-subΓ⊢E⇒A⇝e
A <: B ⇝ co
Γ ⊢ E ⇐ B ⇝ co e

Figure 3.4: Bidirectional type system of λ+i

2017, Figure 3]. Disjointness axioms A ∗ax B (appearing in rule D-ax) take care of two types
with different type constructors (e.g., Int and records). The disjointness relation is helpful tocheck whether the merge of two expressions of type A and B preserves coherence, e.g., it ruleout ambiguous expressions such as 1 , , 2 because Int is not disjoint to Int.

. Syntax and Semantics of λco
We elaborate well-typed source expression E into target terms e. Our target language λco isthe standard simply-typed call-by-value lambda calculus extended with products and coercions. The syntax of λco is shown in Fig. 3.6. The meta-function | · | shown in Definition 5transforms λ+i types to λco types. It is worth pointing out that we use the erasure semantics
to model record labels, i.e., labels are erased during elaboration; this is different from theoriginal publication [Bi et al. 2018] where records are also present in the target. Both work43

3 Semantics of the λ+i Calculus
A∗B(Disjointness)

D-topL

D-topR

⊤∗A

A∗⊤

D-arr
A2 ∗ B2
A1 → A2 ∗ B1 → B2

D-rcdEqA∗B{l : A} ∗ {l : B}

D-andRA ∗ B1A ∗ B2A ∗ B1 & B2

D-andLA1 ∗ BA2 ∗ B
A1 & A2 ∗ B

D-rcdNeql1 ̸= l2
{l1 : A} ∗ {l2 : B}

D-ax
A ∗ax BA∗B

A ∗ax B

(Disjointness axioms)

Dax-symB ∗ax AA ∗ax B

Dax-intArr

Dax-intRcd

Dax-arrRcd

Int ∗ax A1 → A2

Int ∗ax {l : A}

A1 → A2 ∗ax {l : B}

Figure 3.5: Disjointness rules of λ+
i
Types
Terms
CoercionsτecoValuesTyping contexts
Evaluation contextsv
ΨE

Int | ⟨⟩ | τ1 × τ2 | τ1 → τ2
x | i | ⟨⟩ | λx. e | e1 e2 | ⟨e1 , e2 ⟩ | co e
id | co1 ◦ co2 | top | co1 → co2 | ⟨co1 , co2 ⟩ | π1 | π2
dist→ | top→⟨⟩ | i | λx. e | ⟨v1 , v2 ⟩ | (co1 → co2 ) v | dist→ v | top→ v• | Ψ, x : τ[·] | E e | v E | ⟨E, e⟩ | ⟨v, E⟩ | co E

::=
::=
::=
|
::=
::=
::=

Figure 3.6: Syntax of λcofine in λ+i , but discarding records makes the target calculus a bit simpler. The notation | · |is also overloaded for translating source contexts Γ to target contexts Ψ.
Definition 5 (Type translation | · | from λ+i to λco ).
|Int|

=

Int

|⊤|

=

⟨⟩

|A → B|=|A| → |B|

|A & B|=|A| × |B||{l : A}|=|A|

.. Explicit Coercions and Coercive Subtyping
The separate syntactic category for explicit coercions is a distinctive difference from the priorwork [Alpuim et al. 2017; Oliveira et al. 2016] (in which they are regular terms). Our coercions are based on those of Henglein [1994], and we add more forms due to our extra
subtyping rules. Metavariable co (hence the co in λco ) ranges over coercions. As a cognitive44

3.4 Syntax and Semantics of λco

Coercion

Term

Coercion

Term

id
top
π1
⟨co1 , co2 ⟩
top→

λx. x
λx. ⟨⟩
λx. π1 x
λx. ⟨co1 x, co2 x⟩
λx. λy. ⟨⟩co1 ◦ co2
co1 → co2
π2
dist→

λx. co1 (co2 x)
λf. λx. co2 (f (co1 x))λx. π2 x
λx. λy. ⟨(π1 x) y, (π2 x) y⟩

Table 3.1: Correspondence between coercions and terms

aid, we can mentally “desugar” coercions to regular terms, which might help understandthe dynamic semantics of coercions. The correspondence between coercions and terms isshown in Table 3.1. In essence, coercions express the conversion of a term from one typeto another. Because of the addition of coercions, the grammar contains explicit coercion
applications co e as a term, and various unsaturated coercion applications as values. Explicit
coercions are useful for the new semantic proof of coherence based on logical relations. The
subtyping judgment in Fig. 3.3 has the form A <: B ⇝ co, which says that subtyping A <: B
produces a coercion co that converts terms of type |A| to type |B|. Each subtyping rule has
its own specific form of coercion.

.. Typing of λco
The typing of λco has the form Ψ ⊢ e : τ , and is entirely standard. Only the typing of coercion
applications, shown below, deserves attention:
t-cappΨ ⊢ e : τ1

co :: τ1 ▷ τ2

Ψ ⊢ co e : τ2
Here the judgment co :: τ1 ▷ τ2 expresses the typing of coercions, which are essentiallyfunctions from τ1 to τ2 . Their typing rules correspond exactly to the subtyping rules of λ+i ,as shown in Fig. 3.7.

.. Dynamic Semantics
The dynamic semantics of λco is shown in Fig. 3.8. We write e −→ e′ for reduction of expressions. The first three lines are reduction rules for coercions. They do not contribute tocomputation but merely rearrange coercions. Our coercion reduction rules are quite standard but not efficient in terms of space. Nevertheless, there is existing work on space-efficient
coercions [Herman et al. 2010; Siek et al. 2015a], which should be applicable to our work as

45

3 Semantics of the λ+i Calculus(Coercion typing)co :: τ1 ▷ τ2
ct-trans

ct-reflco1 :: τ2 ▷ τ3

co2 :: τ1 ▷ τ2

co1 ◦ co2 :: τ1 ▷ τ3

id :: τ ▷ τ
ct-arrco1 :: τ1′ ▷ τ1

ct-topct-topArr

top :: τ ▷ ⟨⟩top→ :: ⟨⟩ ▷ ⟨⟩ → ⟨⟩ct-pairco2 :: τ2 ▷ τ2′

co1 :: τ1 ▷ τ2

co1 → co2 :: τ1 → τ2 ▷ τ1′ → τ2′

co2 :: τ1 ▷ τ3

⟨co1 , co2 ⟩ :: τ1 ▷ τ2 × τ3

ct-projl

π1 :: τ1 × τ2 ▷ τ1

ct-projr

ct-distArr

π2 :: τ1 × τ2 ▷ τ2

dist→ :: (τ1 → τ2 ) × (τ1 → τ3 ) ▷ τ1 → τ2 × τ3

Figure 3.7: Coercion typing

well. Rule r-app is the usual β-rule that performs actual computation, and rule r-ctxt handles reduction under an evaluation context. As standard, −→∗ is the reflexive, transitive
closure of −→. Now we can show that λco is type safe:
Theorem 3.1 (R
R

Theorem 3.2 (e −→ e′ .

Preservation). If • ⊢ e : τ and e −→ e′ , then • ⊢ e′ : τ .
Progress). If • ⊢ e : τ , then either e is a value, or there exists e′ such that

.. Elaboration Semantics
We are now in a position to explain the elaboration judgments Γ ⊢ E ⇒ A ⇝ e andΓ ⊢ E ⇐ A ⇝ e in Fig. 3.4. The only interesting rule is rule T-sub, which applies thecoercion co produced by subtyping to the target term e to form a coercion application co e.
All the other rules do straightforward translations between source and target expressions.

RR

To conclude, we show two lemmas that relate source expressions to target terms.
Lemma 3.3 (Coercions preserve types). If A <: B ⇝ co, then co :: |A| ▷ |B|.

Proof. By structural induction on the derivation of subtyping.
Lemma 3.4 (Elaboration soundness). We have that:• If Γ ⊢ E ⇒ A ⇝ e, then |Γ| ⊢ e : |A|.
• If Γ ⊢ E ⇐ A ⇝ e, then |Γ| ⊢ e : |A|.

46

3.5 Comparison with λie −→ e′(Single-step reduction)

r-id

r-trans

r-top

r-topArr

id v −→ v(co1 ◦ co2 ) v −→ co1 (co2 v)

top v −→ ⟨⟩(top→ ⟨⟩) ⟨⟩ −→ ⟨⟩r-pairr-arr⟨co1 , co2 ⟩ v −→ ⟨co1 v, co2 v⟩((co1 → co2 ) v1 ) v2 −→ co2 (v1 (co1 v2 ))r-distArr

r-projlr-projr(dist→ ⟨v1 , v2 ⟩) v3 −→ ⟨v1 v3 , v2 v3 ⟩π1 ⟨v1 , v2 ⟩ −→ v1

π2 ⟨v1 , v2 ⟩ −→ v2

r-ctxtr-app

e −→ e′(λx. e) v −→ [v/x]e

E[e] −→ E[e′ ]Figure 3.8: Dynamic semantics of λco

Proof. By structural induction on the derivation of typing.
As a corollary, λ+i is type safe due to Theorems 3.1 and 3.2 and Lemma 3.4.

. Comparison with λi
In this section we identify major differences from λi (cf. Fig. 2.1), which, when taken together, yield a simpler and more elegant system. The differences may seem superficial, butthey have a strong effect on coherence, our major topic in Chapter 5.
No ordinary types. Apart from the extra subtyping rules, there is an important difference
from the λi subtyping relation. The subtyping relation of λi employs an auxiliary unary relation “A ordinary” (cf. rules Si-andL and Si-andR in Fig. 2.1). Ordinary types are employed
to ensure determinism of subtyping (hence uniqueness of the elaboration), which plays afundamental role for ensuring coherence and obtaining an algorithm. In λ+i , we show that
determinism is too strong of a requirement. As we shall see in Chapter 5, it suffices to basethe notion of coherence on contextual equivalence. As such, the λ+i calculus discards thenotion of ordinary types completely; this yields a clean and elegant formulation of the subtyping relation. Another minor difference is that due to the addition of the transitivity axiom(rule S-trans), rules S-andL and S-andR are simplified: an intersection type A1 & A2 is asubtype of both A1 and A2 , instead of the more general form A1 & A2 <: A3 .

47

3 Semantics of the λ+i Calculus
No top-like types. Another notable difference from the coercive subtyping of λi is that,
because of the syntactic proof method, they have a special treatment for coercions of top-liketypes (see the coercion parts in rules Si-andL and Si-andR in Fig. 2.1). Top-like types will
introduce non-determinism during subtyping, thus would potentially endanger coherence.
However, as Oliveira et al. [2016] observed, any coercions for top-like types are unique, even
if multiple derivations exist. For λ+i , as with ordinary types, we do not need this kind of
ad-hoc treatment; for subtyping purposes, top-like types are handled like all other types.
However, as we shall see in Chapter 4, top-like types are useful in the disjointness relation
when we add the bottom type.
No well-formedness judgment. A key difference from the type system of λi is the complete omission of the well-formedness judgment Γ ⊢ A, which appears in rule Ti-abs (ourrule T-abs) and rule Ti-sub (our rule T-sub). The sole purpose of this judgment is to ensure that all intersection types are disjoint. However, as Chapter 5 will explain, this is unnecessary for coherence, and merely complicates the type system. Thus λ+i discards thiswell-formedness judgment altogether in favor of a simpler design that is still coherent. As
a consequence, λ+i supports unrestricted intersection types, and already subsumes λi even
without BCD subtyping: an expression such as 1 : Int & Int is accepted in λ+i but rejectedin λi . This simplification is based on an important observation: incoherence can only arise
from merges. Therefore disjointness checking is only necessary in rule T-merge.

. Algorithmic Subtyping
This section considers the algorithmic aspects of λ+i . The bidirectional type system is syntax
directed, so the only source of non-determinism comes from the subtyping relation. In thissection, we present an algorithm that implements the subtyping relation. While BCD subtyping is powerful, the presence of a transitivity axiom in the rules means that the subtyping
relation is not algorithmic. This raises an obvious question: how to obtain an algorithm forthis subtyping relation? Laurent [2012b] has shown that simply dropping the transitivity
rule from BCD subtyping is not possible without losing expressiveness. Hence, this avenue
for obtaining an algorithm is not available. In a 2012 article [Laurent 2012a], Laurent defined BCD subtyping without transitivity, but the system still does not deliver an algorithm.
Only quite recently, Laurent [2018] presents a general approach to defining a BCD-like subtyping relation that enjoys the “sub-formula property” (read decidability). We adapt Pierce’s
decision procedure [Pierce 1989] for a subtyping system (closely related to BCD) to obtain asound and complete algorithm for our BCD extension. Our algorithm extends Pierce’s deci-48

3.6 Algorithmic Subtyping
Q ⊢ A ≺: B ⇝ coA-and

Q ⊢ A ≺: B1 ⇝ co1(Algorithmic subtyping)Q ⊢ A ≺: B2 ⇝ co2

Q ⊢ A ≺: B1 & B2 ⇝ JQK& ◦ ⟨co1 , co2 ⟩A-arr

Q, B1 ⊢ A ≺: B2 ⇝ co

Q ⊢ A ≺: B1 → B2 ⇝ co

A-rcd

A-top

A-int

Q ⊢ A ≺: {l : B} ⇝ co

Q ⊢ A ≺: ⊤ ⇝ JQK⊤ ◦ top[] ⊢ Int ≺: Int ⇝ id

Q, l ⊢ A ≺: B ⇝ co

A-arrInt[] ⊢ A ≺: A1 ⇝ co1

Q ⊢ A2 ≺: Int ⇝ co2

A, Q ⊢ A1 → A2 ≺: Int ⇝ co1 → co2

A-rcdIntQ ⊢ A ≺: Int ⇝ col, Q ⊢ {l : A} ≺: Int ⇝ co

A-andInt

A-andInt

Q ⊢ A1 & A2 ≺: Int ⇝ co ◦ π1

Q ⊢ A1 & A2 ≺: Int ⇝ co ◦ π2

Q ⊢ A1 ≺: Int ⇝ co

Q ⊢ A2 ≺: Int ⇝ co

Figure 3.9: Algorithmic subtyping of λ+ision procedure with subtyping of records and coercions. We prove in Coq that the algorithm
is sound and complete with respect to the declarative specification. At the same time we findsome errors and missing lemmas in Pierce’s original manual proofs.

.. The Subtyping Algorithm
While Fig. 3.3 is a fine specification of how subtyping should behave, it cannot be read directly
as a subtyping algorithm for two reasons: (1) the conclusions of rules S-refl and S-transoverlap with the other rules, and (2) the premises of rule S-trans mention a type that does
not appear in the conclusion. Simply dropping the two offending rules from the system is
not possible without losing expressivity Laurent [2012b]. Thus we need a different approach.
Figure 3.9 shows the algorithmic subtyping judgment Q ⊢ A ≺: B ⇝ co. This judgment
is the algorithmic counterpart of the declarative judgment A <: Q ⇒ B ⇝ co, where Q
stands for a queue used to track domain types and labels. Definition 6 converts Q ⇒ A to avalid type. For instance, if Q = A, B, {l}, then Q ⇒ C abbreviates A → B → {l : C}.
Definition 6. Q ⇒ A is inductively defined as follows:49

3 Semantics of the λ+i Calculus[] ⇒ A=A(B, Q) ⇒ A=

B → (Q ⇒ A)

(l, Q) ⇒ A={l : Q ⇒ A}

The basic idea of Q ⊢ A ≺: B ⇝ co is to first perform a structural analysis of B, which
descends into both sides of & ’s (rule A-and), into the right side of →’s (rule A-arr), andinto the fields of records (rule A-rcd) until it reaches one of the two base cases, ⊤ or Int.
If the base case is ⊤, the subtyping holds trivially (rule A-top). If the base case is Int, thealgorithm performs a structural analysis of A, in which Q plays an important role. The left
sides of →’s are pushed onto Q as they are encountered in B and popped off again later, left
to right, as →’s are encountered in A (rule A-arrInt). Similarly, the labels are pushed onto
Q as they are encountered in B and popped off again later, left to right, as records are encountered in A (rule A-rcdInt). The remaining two rules A-andInt and A-andInt aresimilar to their declarative counterparts. Let us illustrate the algorithm in Fig. 3.10 with an
example derivation (for formatting reasons we use I and S to denote Int and String respectively), which is essentially the one used by the add field in Section 3.2. The reader can try togive a corresponding derivation using the declarative subtyping and see how rule S-transplays an essential role there.
Remark. Our algorithmic rules are still not deterministic (rules A-andInt and A-andInt
are overlapping). In other words, to check whether A1 & A2 ≺: Int, we need to check if
A1 ≺: Int or A2 ≺: Int. In our prototype, this is implemented using backtracking.
Now consider the coercions. Algorithmic subtyping uses the same set of coercions as
declarative subtyping. However, because algorithmic subtyping has a different structure, therules generate slightly more complicated coercions. Two meta-functions J·K⊤ and J·K& usedin rules A-top and A-and respectively, are meant to generate correct forms of coercions.
They are defined recursively on Q and are shown in Fig. 3.11.

.. Correctness of the Algorithm
To establish the correctness of the algorithm, we must show that the algorithm is both soundand complete with respect to the declarative specification. While soundness follows quite
easily, completeness is much harder. The proof of completeness essentially follows that of
Pierce [1989] in that we need to show the algorithmic subtyping is reflexive and transitive.
Soundness of the algorithm. The following two lemmas connect the declarative subtyping with the meta-functions.

50

3.6 Algorithmic SubtypingDD′{l}, I & S, I & S ⊢ {l : I → I → I} & {l : S → S → S} ≺: I & S

A-and
A-arr

{l}, I & S ⊢ {l : I → I → I} & {l : S → S → S} ≺: I & S → I & S{l} ⊢ {l : I → I → I} & {l : S → S → S} ≺: I & S → I & S → I & S

A-arr
A-rcd

[] ⊢ {l : I → I → I} & {l : S → S → S} ≺: {l : I & S → I & S → I & S}[] ⊢ I ≺: I[] ⊢ I ≺: I[] ⊢ I & S ≺: I[] ⊢ I & S ≺: I[] ⊢ I ≺: I

I & S ⊢ I → I ≺: I

A-arrInt

I & S, I & S ⊢ I → I → I ≺: I

A-rcdInt{l}, I & S, I & S ⊢ {l : I → I → I} ≺: I{l}, I & S, I & S ⊢ {l : I → I → I} & {l : S → S → S} ≺: I

D=

A-andInt[] ⊢ S ≺: S[] ⊢ S ≺: S[] ⊢ I & S ≺: S[] ⊢ I & S ≺: S[] ⊢ S ≺: S

I & S ⊢ S → S ≺: S

A-arrInt

I & S, I & S ⊢ S → S → S ≺: S

A-rcdInt{l}, I & S, I & S ⊢ {l : S → S → S} ≺: S
D′ ={l}, I & S, I & S ⊢ {l : I → I → I} & {l : S → S → S} ≺: S

A-andInt

Figure 3.10: Example derivation

J[]K⊤ = top

J[]K& = id

Jl, QK⊤ = JQK⊤ ◦ id

Jl, QK& = JQK& ◦ id

JA, QK⊤ = (top → JQK⊤ ) ◦ top→

JA, QK& = (id → JQK& ) ◦ dist→

Figure 3.11: Meta-functions of coercionsR). ⊤ <: Q ⇒ ⊤ ⇝ JQK⊤

Lemma 3.5 (Proof. By induction on the length of Q.

R). (Q ⇒ A) & (Q ⇒ B) <: Q ⇒ (A & B) ⇝ JQK&

Lemma 3.6 (Proof. By induction on the length of Q.
The proof of soundness is straightforward.

R

Theorem 3.7 (Soundness). If Q ⊢ A ≺: B ⇝ co then A <: Q ⇒ B ⇝ co.

Proof. By induction on the derivation of the algorithmic subtyping and applying Lemmas 3.5
and 3.6 where appropriate.

51

3 Semantics of the λ+i Calculus
Completeness of the algorithm. Completeness, however, is much harder. The reason
is that, due to the use of Q, reflexivity and transitivity are not entirely obvious. We need tostrengthen the induction hypothesis by introducing the notion of a set, U(A), of “reflexive
supertypes” of A, as defined below:
U(⊤) ≜ {⊤}U(Int) ≜ {Int}U({l : A}) ≜ {{l : B} | B ∈ U (A)}U(A & B) ≜ U(A) ∪ U(B) ∪ {A & B}

U(A → B) ≜ {A → C | C ∈ U (B)}R
R

We show two lemmas about U(A) that are crucial in the subsequent proofs.
Lemma 3.8 (). A ∈ U (A)Proof. By induction on the structure of A.
Lemma 3.9 (). If A ∈ U (B) and B ∈ U (C), then A ∈ U (C).

Proof. By induction on the structure of B.
Remark. Lemma 3.9 is not found in Pierce’s proofs [Pierce 1989], which is crucial in Lemma 3.10,

Rfrom which reflexivity (Lemma 3.11) follows immediately.
Lemma 3.10 (). If Q ⇒ B ∈ U (A) then there exists co such that Q ⊢ A ≺: B ⇝ co.

Proof. By induction on size(A) + size(B) + size(Q).

R

Now it immediately follows that the algorithmic subtyping is reflexive.
Lemma 3.11 (Reflexivity). For every A there exists co such that [] ⊢ A ≺: A ⇝ co.

Proof. Immediate from Lemma 3.8 and Lemma 3.10.
The proof of transitivity is, to quote Pierce [1989], typically “the hardest single piece” ofRmetatheory. We omit the details here and refer the interested reader to our Coq development.
Lemma 3.12 (Transitivity). If [] ⊢ A1 ≺: A2 ⇝ co1 and [] ⊢ A2 ≺: A3 ⇝ co2 , thenthere exists co such that [] ⊢ A1 ≺: A3 ⇝ co.

R

With reflexivity and transitivity in position, we show the main theorem.
Theorem 3.13 (B ⇝ co′ .

Completeness). If A <: B ⇝ co then there exists co′ such that [] ⊢ A ≺:Proof. By induction on the derivation of the declarative subtyping and applying Lemmas 3.11
and 3.12 where appropriate.

52

3.6 Algorithmic Subtyping
Remark. Pierce’s proof is wrong [Pierce 1989, pp. 20, Case F] in the caseS-arr

B1 <: A1 ⇝ co1

A2 <: B2 ⇝ co2

A1 → A2 <: B1 → B2 ⇝ co1 → co2
where he concludes from the inductive hypotheses [] ⊢ B1 ≺: A1 and [] ⊢ A2 ≺: B2 that[] ⊢ A1 → A2 ≺: B1 → B2 (his rules 6a and 3). However his rule 6a (our rule A-arrInt)
only works for primitive types, and is thus not applicable in this case. Instead we need a few
technical lemmas to support the argument.

53 Semantics of the F Calculus+iThis chapter introduces F+
i , an explicitly typed second-order lambda calculus with disjoint+
quantification and disjoint intersection types. F+
i can be characterized as the hybrid of λi(which does not support polymorphism) and Fi (which does not support BCD subtyping):
F+
i
λ+
i

Fi

λi
As we will see, the combination is very expressive, enabling improved compositional designs and supporting automated composition of interpretations in programming techniques
like object algebras [Oliveira and Cook 2012] and finally tagless [Carette et al. 2009]. Furthermore, as we will show in Chapter 7, F+
i is able to encode sophisticated concepts suchas mixins/traits and dynamic inheritance. Unfortunately, the combination also introduces
non-trivial complications. The main technical challenge (like for most other calculi withdisjoint intersection types) is the proof of coherence—our main topic in Chapter 6.
The formalization and metatheory of F+
i are a significant advance over its predecessor Fi .
Besides the support for distributive subtyping, F+i removes several restrictions imposed by
the syntactic coherence proof in Fi . In particular F+
i supports unrestricted intersections,which are forbidden in Fi . Unrestricted intersections enable, for example, encoding certain
forms of bounded quantification [Pierce 1991]. Moreover the new proof method is morerobust with respect to language extensions. For instance, F+i supports the bottom type without significant complications in the proofs, while it was a challenging open problem in Fi .
A final interesting aspect is that F+
i ’s type-checking is decidable. In the design space of languages with polymorphism and subtyping, similar mechanisms have been known to lead toundecidability. Pierce’s seminal paper “Bounded quantification is undecidable” [Pierce 1994]
shows that the contravariant subtyping rule for bounded quantification in F<: leads to undecidability of subtyping. In F+
i the contravariant rule for disjoint quantification retains de-55

4 Semantics of the F+
i Calculus
cidability. Since with unrestricted intersections F+
i can express several use cases of bounded
quantification, F+i could be an interesting and decidable alternative to F<: .

. Motivation: Compositional Programming
To demonstrate the compositional properties of F+
i we use Gibbons and Wu’s shallow embeddings of parallel prefix circuits [Gibbons and Wu 2014]. By means of several different
shallow embeddings, we first illustrate the short-comings of a state-of-the-art compositional
approach, namely a finally tagless encoding [Carette et al. 2009] in Haskell. Next we show
how parametric polymorphism and distributive intersection types provide a more elegant
and compact solution in F+
i (with a few convenient source level constructs).

.. A Finally Tagless Encoding in Haskell
The circuit DSL represents networks that map a number of inputs (known as the width) of
some type A onto the same number of outputs of the same type. The outputs combine (withrepetitions) one or more inputs using a binary associative operator ⊕ : A × A → A. A
particularly interesting class of circuits that can be expressed in the DSL are parallel prefix
circuits. These represent computations that take n > 0 inputs x1 , . . . , xn and produce n
outputs y1 , . . . , yn , where yi = x1 ⊕ x2 ⊕ . . . ⊕ xi .
The DSL features 5 language primitives: two basic circuit constructors and three circuit
combinators. These are captured in the Haskell type class Circuit:class Circuit c where
identity :: Int → cfan:: Int → cbeside:: c → c → c
above:: c → c → cstretch :: [Int] → c → c

An identity circuit with n inputs xi , has n outputs yi = xi . A fan circuit has n inputs xi andn outputs yi , where y1 = x1 and yj = x1 ⊕ xj (j > 1). The binary beside combinator puts
two circuits in parallel; the combined circuit takes the inputs of both circuits to the outputsof both circuits. The binary above combinator connects the outputs of the first circuit to
the inputs of the second; the width of both circuits has to be same. Finally, stretch ws cinterleaves the wires of circuit c with bundles of additional wires that map their input straighton their output. The ws parameter specifies the width of the consecutive bundles; the ith wireof c is preceded by a bundle of width wsi − 1.

56

4.1 Motivation: Compositional Programming

newtype Width = W {width :: Int}

newtype Depth = D {depth :: Int}instance Circuit Width where
identity n
= W n
fan n
= W n
beside c1 c2 =W (width c1 + width c2)
above c1 c2 = c1
stretch ws c = W (sum ws)

instance Circuit Depth where
identity n
= D 0
fan n
= D 1
beside c1 c2 =D (max (depth c1) (depth c2))
above c1 c2 = D (depth c1 + depth c2)
stretch ws c = c(a) Width embedding(b) Depth embedding

Figure 4.1: Two finally tagless embeddings of circuits.

Basic width and depth embeddings. Figure 4.1 shows two simple shallow embeddings,
which represent a circuit respectively in terms of its width and its depth. The former denotes the number of inputs/outputs of a circuit, while the latter is the maximal number of⊕ operators between any input and output. Both definitions follow the same setup: a new
Haskell datatype (Width/Depth) wraps the primitive result value and provides an instance of
the Circuit type class that interprets the 5 DSL primitives accordingly. The following code
creates a so-called Brent-Kung parallel prefix circuit [Brent and Kung 1980]:
e1 :: Widthe1 = above (beside (fan 2) (fan 2))(above (stretch [2, 2] (fan 2))(beside (beside (identity 1) (fan 2)) (identity 1)))

Here e1 evaluates to W {width = 4}. If we want to know the depth of the circuit, we have tochange type signature to Depth.
Interpreting multiple ways. Fortunately, with the help of polymorphism we can define
a type of circuits that support multiple interpretations at once.
type DCircuit = forall c. Circuit c ⇒ cThis way we can provide a single Brent-Kung parallel prefix circuit definition that can bereused for different interpretations.
brentKung :: DCircuit
brentKung = above (beside (fan 2) (fan 2))(above (stretch [2, 2] (fan 2))(beside (beside (identity 1) (fan 2)) (identity 1)))

A type annotation then selects the desired interpretation. For instance, brentKung :: Widthyields the width and brentKung :: Depth the depth.

57

4 Semantics of the F+
i Calculus
Composition of embeddings. What is not ideal in the above code, however, is that thesame brentKung circuit is processed twice, if we want to execute both interpretations. We
can do better by processing the circuit only once, computing both interpretations simultaneously. The finally tagless encoding achieves this with a boilerplate instance for tuples ofinterpretations.
instance (Circuit c1, Circuit c2) ⇒ Circuit (c1, c2) where
identity n= (identity n, identity n)
fan n= (fan n, fan n)beside c1 c2 = (beside (fst c1) (fst c2), beside (snd c1) (snd c2))
above c1 c2= (above (fst c1) (fst c2), above (snd c1) (snd c2))
stretch ws c = (stretch ws (fst c), stretch ws (snd c))

Now we can get both embeddings simultaneously as follows:
e12 :: (Width, Depth)
e12 = brentKung

This evaluates to (W {width = 4}, D {depth = 2}).
Composition of dependent interpretations. The composition above is easy because
the two embeddings are orthogonal. In contrast, the composition of dependent interpretations is rather cumbersome in the standard finally tagless setup. An example of the latteris the interpretation of circuits as their well-sizedness, which captures whether circuits are
well-formed. This interpretation depends on the interpretation of circuits as their width.1
data WellSized = WS { wS :: Bool, ox :: Width }instance Circuit WellSized where
identity n= WS True (identity n)
fan n= WS True (fan n)beside c1 c2 = WS (wS c1 && wS c2) (beside (ox c1) (ox c2))
above c1 c2 = WS (wS c1 && wS c2 && width (ox c1) == width (ox c2))
(above (ox c1) (ox c2))
stretch ws c = WS (wS c && length ws==width (ox c)) (stretch ws (ox c))

The WellSized datatype represents the well-sizedness of a circuit with a Boolean, and also
keeps track of the circuit’s width. The 5 primitives compute the well-sizedness in terms ofboth the width and well-sizedness of the subcomponents. What makes the code cumbersome is that it has to explicitly delegate to the Width interpretation to collect this additional
information.
1

Dependent recursion schemes are also known as zygomorphism [Fokkinga 1989] after the ancient Greek wordζυγον for yoke. We have labeled the Width field with ox because it is pulling the yoke.

584.1 Motivation: Compositional Programming
With the help of a substantially more complicated setup that features a dozen Haskell language extensions, and advanced programming techniques, we can make the explicit delegation implicit (see Appendix A). Nevertheless, that approach still requires a lot of boilerplatethat needs to be repeated for each DSL, as well as explicit projections that need to be written
in each interpretation. A final remark is that adding new primitives (e.g., a “right stretch”
rstretch combinator [Hinze 2004]) can also be easily achieved in the finally tagless approach(e.g., see Oliveira et al. [2006]).

.. The F+
i Encoding
The F+
i setup of the circuit DSL is similar to the finally tagless approach. Instead of a Circuit
c type class, there is a Circuit[C] type that gathers the 5 circuit primitives in a record. Likein Haskell, the type parameter C expresses that the interpretation of circuits is a parameter.
type Circuit[C] = {identity : Int → C,
fan
: Int → C,
beside: C → C → C,above
: C → C → C,
stretch : List[Int] → C → C};

As a side note if a new constructor (e.g., rstretch) is needed, then this is done by means ofintersection types in F+
i :type NCircuit[C] = Circuit[C] & { rstretch : List[Int] → C → C };Basic width and depth embeddings. Figure 4.2 shows the two basic shallow embeddings for width and depth. In both cases, a named F+
i definition replaces the corresponding
unnamed Haskell type class instance in providing the implementations of the 5 language
primitives for a particular interpretation.
The use of the F+
i embeddings is different from that of their Haskell counterparts. Where
Haskell implicitly selects the appropriate type class instance based on the available type information, in F+
i the programmer explicitly selects the implementation following the styleused by object algebras. The following code does this by building a circuit with l1 (short for
language1).
l1 = language1;
e1 = l1.above (l1.beside (l1.fan 2) (l1.fan 2))(l1.above (l1.stretch (cons 2 (cons 2 nil)) (l1.fan 2))(l1.beside (l1.beside (l1.identity 1) (l1.fan 2)) (l1.identity 1)));59

4 Semantics of the F+
i Calculus
type Width = { width : Int };language1 : Circuit[Width] = {identity (n : Int) = { width = n },fan
(n : Int) = { width = n },beside (c1 : Width) (c2 : Width) = { width = c1.width + c2.width },above (c1 : Width) (c2 : Width) = { width = c1.width },stretch (ws : List[Int]) (c : Width) = { width = sum ws }};type Depth = { depth : Int };language2 : Circuit[Depth] = {identity (n : Int) = { depth = 0 },fan(n : Int) = { depth = 1 },beside (c1 : Depth) (c2 : Depth) = { depth = max c1.depth c2.depth },above (c1 : Depth) (c2 : Depth) = { depth = c1.depth + c2.depth },stretch (ws : List[Int]) (c : Depth) = { depth = c.depth }};Figure 4.2: Two F+
i embeddings of circuits.

Here e1 evaluates to {width = 4}. If we want to know the depth of the circuit, we have toreplicate the code with language2.
Dynamically reusable circuits. Just like in Haskell, we can use polymorphism to define a type of circuits that can be interpreted with different languages.
type DCircuit = { accept : forall C. Circuit[C] → C };In contrast to the Haskell solution, this implementation explicitly accepts the implementation.
brentKung : DCircuit = {accept C l = l.above (l.beside (l.fan 2) (l.fan 2))(l.above (l.stretch (cons 2 (cons 2 nil)) (l.fan 2))(l.beside (l.beside (l.identity 1) (l.fan 2)) (l.identity 1)))};l2 = language2;
e1 = brentKung.accept Width l1;e2 = brentKung.accept Depth l2;

Automatic composition of languages. Of course, like in Haskell we can also computeboth results simultaneously. However, unlike in Haskell, the composition of the two inter-60

4.1 Motivation: Compositional Programming
pretation requires no boilerplate whatsoever—in particular, there is no F+
i counterpart of
the Circuit (c1, c2) instance. Instead, we can just compose the two interpretations withthe term-level merge operator (,,) and specify the desired type Circuit[Width & Depth].
l3 : Circuit[Width & Depth] = l1 ,, l2;e3 = brentKung.accept (Width & Depth) l3;

Here the use of the merge operator creates a term with the intersection type Circuit[Width]& Circuit[Depth]. Implicitly, the F+i type system takes care of the details, turning thisintersection type into Circuit[Width & Depth]. This is possible because intersection (&)
distributes over function and record types.
Composition of dependent interpretations. In F+
i the composition scales nicely todependent interpretations. For instance, the well-sizedness interpretation can be expressed
without explicit projections.
type WellSized = { wS : Bool };language3 = {identity (n : Int) = { wS = true },fan(n : Int) = { wS = true },above (c1 : WellSized & Width) (c2 : WellSized & Width) ={ wS = c1.wS && c2.wS && c1.width == c2.width },beside (c1 : WellSized) (c2 : WellSized) = { wS = c1.wS && c2.wS
stretch (ws : List[Int]) (c : WellSized & Width) ={ wS = c.wS && length ws == c.width }};},Here the WellSized & Width type in the above and stretch cases expresses that both thewell-sizedness and width of subcircuits must be given, and that the width implementationis left as a dependency—when language3 is used, then the width implementation must be
provided. Again, the distributive properties of & in the type system take care of merging thetwo interpretations.
l4= language1 ,, language3;e4= brentKung.accept (WellSized & Width) l4;
main = e4.wS -- Output: true

Disjoint polymorphism and dynamic merges. While it may seem from the above examples that definitions have to be merged statically, F+
i in fact supports dynamic merges. For
instance, we can encapsulate the merge operator in the combine function while abstracting
over the two components x and y that are merged as well as over their types A and B.

61

4 Semantics of the F+
i Calculus
Types
Expressions

A, B, CE

Term contexts
Type contexts

Γ
∆

::=
::=
|
::=
::=

Int | ⊤ | ⊥ | A → B | A & B | {l : A} | α | ∀(α ∗ A). B
x | i | ⊤ | λx. E | E1 E2 | E1 , , E2 | E : A | {l = E} | E.lΛ(α ∗ A). E | E A• | Γ, x : A• | ∆, α ∗ A
Figure 4.3: Syntax of F+i

combine A [B * A] (x : A) (y : B) = x ,, y;This way the components x and y are only known at runtime and thus the merge can only
happen at that time. The types A and B cannot be chosen entirely freely. For instance, if bothcomponents would contribute an implementation for the same method, which implementation is provided by the combination would be ambiguous. To avoid this problem the two
types A and B have to be disjoint. This is expressed in the disjointness constraint * A on thequantifier of the type variable B. If a quantifier mentions no disjointness constraint, like thatof A, it defaults to the trivial * ⊤ constraint which implies no restriction. With combine, we
can rewrite l3 as follows (note that Width and Depth are disjoint):
l3 = combine Circuit[Width] Circuit[Depth] language1 language2;

. Syntax and Semantics+
Figure 4.3 shows the syntax of F+i . Metavariables A, B, C range over types. Apart from λitypes, F+i also includes type variables α and disjoint quantification ∀(α ∗ A). B. One novelty in F+
i is the addition of the uninhabited bottom type ⊥. Metavariable E ranges overexpressions. We extend λ+i expressions with two standard constructs from System F: typeabstractions Λ(α ∗ A). E and type applications E A. The former also includes an extra disjointness constraint A associated with the type variable α.

Well-formedness and unrestricted intersections. The well-formedness judgments
for types ∆ ⊢ A in Fig. 4.4 is standard and only enforces well-scoping. This is one of the
key differences from Fi , which (like its predecessor λi ) uses well-formedness to also ensurethat all intersection types are disjoint. In other words, while in Fi all valid intersection types

62

4.2 Syntax and Semantics∆⊢A(Well-formedness of types)

swft-topswft-int∆⊢⊤∆ ⊢ Int

swft-all∆⊢A∆, α ∗ A ⊢ B∆ ⊢ ∀(α ∗ A). B

swft-varswft-arrow(α ∗ A) ∈ ∆

∆⊢A

∆⊢α
swft-and

∆⊢A

∆⊢B

∆⊢A→B
swft-rcd∆⊢B∆⊢A∆ ⊢ A&B∆⊢Γ∆ ⊢ {l : A}

(Well-formedness of value contexts)
swfe-varswfe-empty

∆⊢Γ

∆⊢•

∆⊢A

∆ ⊢ Γ, x : A⊢∆(Well-formedness of type contexts)
swfte-varswfte-empty

⊢∆

⊢•

∆⊢A

⊢ ∆, α ∗ A

Figure 4.4: Well-formedness of types and contexts

must be disjoint, in F+
i unrestricted intersection types such as Int & Int are allowed. Morespecifically, the well-formedness of intersection types in F+
i and Fi is:
wf-F+
i

∆⊢A

wf-Fi

∆⊢B

∆ ⊢ A&B

∆⊢A

∆⊢B

∆⊢A∗B

∆ ⊢ A&B

Notice that Fi has an extra disjointness condition ∆ ⊢ A ∗ B in the premise. This is crucial
for Fi ’s syntactic method for proving coherence, but also burdens the calculus with various
syntactic restrictions and complicates its metatheory. For example, it requires extra effort
to show that Fi only produces disjoint intersection types. As a consequence, Fi features a
weaker substitution lemma (note the gray part in Proposition 4.1) than F+i (Lemma 4.2).
Proposition 4.1 (Types are stable under substitution in Fi ). If ∆ ⊢ A and ∆ ⊢ B and (α ∗C) ∈ ∆ and ∆ ⊢ B ∗ C and well-formed context [B/α]∆, then [B/α]∆ ⊢ [B/α]A.

63

4 Semantics of the F+
i Calculus
A <: B ⇝ co

(Declarative subtyping)
S-trans

S-reflA2 <: A3 ⇝ co1

A <: A ⇝ id

S-top

A1 <: A2 ⇝ co2

A1 <: A3 ⇝ co1 ◦ co2

S-rcd

A <: B ⇝ co{l : A} <: {l : B} ⇝ coS-arr

B1 <: A1 ⇝ co1

A <: ⊤ ⇝ top

S-andL

S-andR

A1 & A2 <: A1 ⇝ π1

A1 & A2 <: A2 ⇝ π2

S-and

A2 <: B2 ⇝ co2

A1 <: A2 ⇝ co1

A1 → A2 <: B1 → B2 ⇝ co1 → co2

A1 <: A3 ⇝ co2

A1 <: A2 & A3 ⇝ ⟨co1 , co2 ⟩S-distArr

S-topArr(A1 → A2 ) & (A1 → A3 ) <: A1 → A2 & A3 ⇝ dist→⊤ <: ⊤ → ⊤ ⇝ top→

S-distRcd

S-topRcd

S-bot{l : A} & {l : B} <: {l : A & B} ⇝ id

⊤ <: {l : ⊤} ⇝ id

⊥ <: A ⇝ bot

S-forallB1 <: B2 ⇝ co

A2 <: A1 ⇝ co′∀(α ∗ A1 ). B1 <: ∀(α ∗ A2 ). B2 ⇝ co∀

S-topAll⊤ <: ∀(α ∗ ⊤). ⊤ ⇝ top∀

S-distAll(∀(α ∗ A). B1 ) & (∀(α ∗ A). B2 ) <: ∀(α ∗ A). B1 & B2 ⇝ dist∀

Lemma 4.2 (R

Figure 4.5: Declarative subtyping of F+i

Types are stable under substitution in F+i ). If ∆ ⊢ A and ∆ ⊢ B and(α ∗ C) ∈ ∆ and well-formed context [B/α]∆, then [B/α]∆ ⊢ [B/α]A.
Proof. By induction on the derivation of ∆ ⊢ A.
+
Declarative subtyping of F+
i . We extend the subtyping of λi with four new rules:

rule S-bot for the bottom type, and rules S-distAll and S-topAll for distributivity of disjoint quantification, as shown in Fig. 4.5. ⊥ is a subtype of all types (rule S-bot). Subtypingof disjoint quantification is covariant in its body, and contravariant in its disjointness constraints (rule S-forall). Rule S-distAll, similar to rule S-distArr, dictates that quantifiers
may distribute over intersections. Rule S-topAll plays a similar role as rule S-topArr. A

64

4.2 Syntax and Semantics∆; Γ ⊢ E ⇒ A ⇝ e(Inference)

FT-top⊢∆∆⊢Γ∆; Γ ⊢ ⊤ ⇒ ⊤ ⇝ ⟨⟩FT-int⊢∆∆⊢Γ∆; Γ ⊢ i ⇒ Int ⇝ i

FT-app∆; Γ ⊢ E1 ⇒ A1 → A2 ⇝ e1∆; Γ ⊢ E2 ⇐ A1 ⇝ e2∆; Γ ⊢ E1 E2 ⇒ A2 ⇝ e1 e2
FT-anno∆; Γ ⊢ E ⇐ A ⇝ e∆; Γ ⊢ E : A ⇒ A ⇝ e

FT-var⊢∆∆⊢Γ(x : A) ∈ Γ∆; Γ ⊢ x ⇒ A ⇝ x

FT-merge∆; Γ ⊢ E1 ⇒ A1 ⇝ e1∆; Γ ⊢ E2 ⇒ A2 ⇝ e2
∆ ⊢ A1 ∗ A2∆; Γ ⊢ E1 , , E2 ⇒ A1 & A2 ⇝ ⟨e1 , e2 ⟩FT-rcd∆; Γ ⊢ E ⇒ A ⇝ e∆; Γ ⊢ {l = E} ⇒ {l : A} ⇝ e

FT-proj∆; Γ ⊢ E ⇒ {l : A} ⇝ e∆; Γ ⊢ E.l ⇒ A ⇝ e

FT-tabs∆, α ∗ A; Γ ⊢ E ⇒ B ⇝ e∆⊢A∆⊢Γ∆; Γ ⊢ Λ(α ∗ A). E ⇒ ∀(α ∗ A). B ⇝ Λα. e
FT-tapp∆; Γ ⊢ E ⇒ ∀(α ∗ B). C ⇝ e∆⊢A∗B∆; Γ ⊢ E A ⇒ [A/α]C ⇝ e |A|∆; Γ ⊢ E ⇐ A ⇝ e(Checking)FT-abs∆⊢A∆; Γ, x : A ⊢ E ⇐ B ⇝ e∆; Γ ⊢ λx. E ⇐ A → B ⇝ λx. e

FT-sub∆; Γ ⊢ E ⇒ B ⇝ e
B <: A ⇝ co∆; Γ ⊢ E ⇐ A ⇝ co e∆⊢A

Figure 4.6: Bidirectional type system of F+iminor comment is that since F+
i features explicit polymorphism, type variables are neutral
to subtyping (i.e., α <: α), which is already contained in rule S-refl.
Typing of F+i .

F+i features a bidirectional type system inherited from Fi . The full set oftyping rules are shown in Fig. 4.6. Again we ignore the translation parts (⇝ e) and explainthem in Section 4.4 The inference judgment ∆; Γ ⊢ E ⇒ A says that we can synthesize
the type A in the contexts ∆ and Γ. The checking judgment ∆; Γ ⊢ E ⇐ A asserts that
E checks against the type A in the contexts ∆ and Γ. Most of the rules are quite standard
in the literature. The merge expression E1 , , E2 is well-typed if both sub-expressions are
well-typed, and their types are disjoint (rule FT-merge). The disjointness relation will be
explained in Section 4.3. To infer a type abstraction (rule FT-tabs), we add disjointness65

4 Semantics of the F+
i Calculus
constraints to the type context. For a type application (rule FT-tapp), we check that the typeargument satisfies the disjointness constraints. Rules FT-merge and FT-tapp are the only
rules checking disjointness.

. Disjointness
We now turn to another core judgment of F+i —the disjointness relation, shown in Fig. 4.7.
The disjointness rules are mostly inherited from Fi [Alpuim et al. 2017], but the new bottom
type requires a notable change regarding disjointness with top-like types.
Top-like types. Top-like types are all types that are isomorphic to ⊤ (i.e., simultaneously
sub- and supertypes of ⊤). Hence, they are inhabited by a single value, isomorphic to the ⊤value. The following rules capture this notion in a syntax-directed fashion:⌉A⌈
TL-top⌉⊤⌈(Top-like types)
TL-and⌉A⌈⌉B⌈⌉A & B⌈

TL-arr

TL-rcd

⌉B⌈

⌉A⌈

⌉B⌈

⌉A → B⌈⌉{l : A}⌈⌉∀(α ∗ A). B⌈

TL-allAs a historical note, the concept of top-like types were already known by Barendregt et al.
[1983] (although they did not call it “top-like”). The λi calculus [Oliveira et al. 2016] rediscovered it and coined the term “top-like types”; the Fi calculus [Alpuim et al. 2017] extended it with universal quantifiers. Note that in both λi and Fi , the introduction of top-liketypes is solely for enabling a syntactic method of proving coherence, and due to the lack of
BCD subtyping, they do not have a type-theoretic interpretation of top-like types.
The disjointness judgment ∆ ⊢ A ∗ B is helpful to check whether the merge of two expressions of type A and B preserves coherence. (As a precondition, A and B are required to
be both well-formed in the context ∆.) Incoherence arises when both expressions produce
distinct values for the same type, either directly when they are both of that same type, or
through implicit upcasting to a common supertype. Of course we can safely disregard toplike types in this matter because they do not have two distinct values. In short, it suffices tocheck that the two types have only top-like supertypes in common.
Because ⊥ and any another type A always have A as a common supertype, it follows that⊥ is only disjoint to A when A is top-like. More generally, if A is a top-like type, then A
is disjoint to any type. This is the rationale behind the two rules FD-topL and FD-topR,
which generalize and subsume ∆ ⊢ ⊤ ∗ A and ∆ ⊢ A ∗ ⊤ from Fi , and also cater to the66

4.3 Disjointness∆⊢A∗B(Disjointness)

FD-topL

FD-topR

FD-arr

∆⊢A∗B

∆⊢A∗B

∆ ⊢ A1 → A2 ∗ B1 → B2

⌉A⌈

FD-andL∆ ⊢ A1 ∗ B⌉B⌈∆ ⊢ A2 ∗ B2

FD-andR∆ ⊢ A2 ∗ B∆ ⊢ A ∗ B1

∆ ⊢ A1 & A2 ∗ B∆ ⊢ A ∗ B2

∆ ⊢ A ∗ B1 & B2

FD-rcdNeq

FD-tvarL

l1 ̸= l2(α ∗ A) ∈ ∆∆ ⊢ {l1 : A} ∗ {l2 : B}

FD-rcdEq∆⊢A∗B∆ ⊢ {l : A} ∗ {l : B}
FD-tvarR

A <: B(α ∗ A) ∈ ∆

∆⊢α∗B

∆⊢B∗α

FD-forallFD-ax∆ ⊢ ∀(α ∗ A1 ). B1 ∗ ∀(α ∗ A2 ). B2∆⊢A∗B∆, α ∗ A1 & A2 ⊢ B1 ∗ B2

A ∗ax BA <: BA ∗ax B

(Disjointness axioms)

Dax-symB ∗ax A

Dax-intArr

Dax-intRcd

Dax-intAll

A ∗ax BInt ∗ax A1 → A2

Int ∗ax {l : A}

Int ∗ax ∀(α ∗ B1 ). B2

Dax-arrAll

Dax-arrRcd

Dax-allRcd

A1 → A2 ∗ax ∀(α ∗ B1 ). B2

A1 → A2 ∗ax {l : B}∀(α ∗ A1 ). A2 ∗ax {l : B}

Figure 4.7: Disjointness rules of F+ibottom type. Two other interesting rules are FD-tvarL and FD-tvarR, which dictate thata type variable α is disjoint with some type B if its disjointness constraints A is a subtype of
B. These two rules are a specialization of a more general lemma [Alpuim et al. 2017], whichRsays that disjointness is covariant with respect to subtyping:
Lemma 4.3 (Covariance of disjointness). If ∆ ⊢ A ∗ B and B <: C, then ∆ ⊢ A ∗ C.

Proof. By double induction, first on the subtyping derivation, and then on the type A. In theRcase for rule S-forall, we need Lemma 4.4.
Lemma 4.4 (Narrowing of disjointness). If ∆, α ∗ C1 ⊢ A ∗ B and C2 <: C1 , then∆, α ∗ C2 ⊢ A ∗ B.

67

4 Semantics of the F+
i Calculus
Types
Expressions
CoercionsτecoValuesv

Value contexts
Type contexts
Evaluation contexts

Ψ
Φ
E

::=
::=
::=
|
::=
|
::=
::=
::=

Int | ⟨⟩ | τ1 → τ2 | τ1 × τ2 | α | ∀α. τ
x | i | ⟨⟩ | λx. e | e1 e2 | ⟨e1 , e2 ⟩ | co e | Λα. e | e τ
id | co1 ◦ co2 | top | co1 → co2 | ⟨co1 , co2 ⟩ | π1 | π2
dist→ | top→ | bot | co∀ | top∀ | dist∀i | ⟨⟩ | λx. e | ⟨v1 , v2 ⟩ | (co1 → co2 ) v | dist→ v | top→ v
Λα. e | co∀ v | top∀ v | dist∀ v• | Ψ, x : τ• | Φ, α[·] | E e | v E | ⟨E, e⟩ | ⟨v, E⟩ | co E | E τ
Figure 4.8: Syntax of Fco

Proof. We need to slightly generalize the lemma in the sense that the type variable is inserted
in the middle, then by induction on the disjointness derivation.
To conclude this section, we show that the disjointness relation is symmetric:
Lemma 4.5 (R

Symmetry of disjointness). If ∆ ⊢ A ∗ B, then ∆ ⊢ B ∗ A.

Proof. By structural induction on the derivation of disjointness. In the case for rule FDforall, apply Lemma 4.4.

. Elaboration and Type Safety+
Like λ+i , the dynamic semantics of Fi is given by elaboration into a target calculus. The

target calculus Fco is the standard call-by-value System F extended with products and coercions. The syntax of Fco is shown in Fig. 4.8, with the differences from λco highlighted . We
extend the type translation function | · | to cover new constructs: ⊥ is mapped to an uninhabited type ∀α. α; disjoint quantification is mapped to universal quantification, dropping
the disjointness constraints.
Definition 7 (Type translation | · | from F+
i to Fco ).

68

|Int|

=

Int

|⊤|

=

⟨⟩

|A → B|=|A| → |B|

|A & B|=|A| × |B||{l : A}|

=

|A|

|α|

=

α

|∀(α ∗ A). B|=∀α. |B|

|⊥|

=

∀α. α

4.4 Elaboration and Type Safety

Coercion

Term

Coercion

Term

id
top
π1
⟨co1 , co2 ⟩
top→

λx. x
λx. ⟨⟩
λx. π1 x
λx. ⟨co1 x, co2 x⟩
λx. λy. ⟨⟩co1 ◦ co2
co1 → co2
π2
dist→
co∀

λx. co1 (co2 x)
λf. λx. co2 (f (co1 x))λx. π2 x
λx. λy. ⟨(π1 x) y, (π2 x) y⟩
λf. Λα. co (f α)top∀

λx. Λα. ⟨⟩dist∀

λf. Λα. ⟨(π1 f) α, (π2 f) α⟩bot

λf. Λα. f α
Table 4.1: Correspondence between coercions and terms, extended

Coercions and coercive subtyping. As shown in Fig. 4.8, we extend the coercions of
λco with several new coercions: bot, co∀ , dist∀ and top∀ due to the addition of polymorphism
and bottom type. As a cognitive aid, we extend Table 3.1 with new desugaring rules, shownin Table 4.1. For example, co∀ can be thought of as λf. Λα. co (f α), then the expression co∀ v
is “equal” to Λα. co (v α), which is why we can treat co∀ v as a value.
Static semantics. Figure 4.9 presents the typing rules of Fco . Most of the rules are aresimilar to . Rule Ft-capp uses the judgment co :: τ1 ▷ τ2 to type coercions. We extend
Fig. 3.7 with four new rules, corresponding to the four new coercions:ct-forall

ct-botco :: τ1 ▷ τ2

bot :: ∀α. α ▷ τco∀ :: ∀α. τ1 ▷ ∀α. τ2ct-topAll

top∀ :: ⟨⟩ ▷ ∀α. ⟨⟩ct-distAll

dist∀ :: (∀α. τ1 ) × (∀α. τ2 ) ▷ ∀α. τ1 × τ2
Dynamic semantics. We extend the evaluation context with one new form E τ for typeapplications, as shown in Fig. 4.8. The set of reduction rules (Fig. 4.10) for Fco is a straightforward extension of λco . We have three new reduction rules r-forall, r-distAll, andr-topAll for the new coercions. Also we add the reduction rule r-tapp for type applica-R
R

tions. Now we can show that Fco is type-safe in the usual sense:
Theorem 4.6 (Preservation of Fco ). If •; • ⊢ e : τ and e −→ e′ , then •; • ⊢ e′ : τ .

Theorem 4.7 (Progress of Fco ). If •; • ⊢ e : τ , then either e is a value, or there exists e′such that e −→ e′ .

69

4 Semantics of the F+
i CalculusΦ⊢Ψ(Well-formedness of value context)
wfe-varΦ⊢τΦ⊢ΨΦ ⊢ Ψ, x : τ

wfe-emptyΦ⊢•Φ⊢τ

(Well-formedness of types)
wft-varα∈ΦΦ⊢α

wft-intΦ ⊢ Int

wft-arrowΦ ⊢ τ1Φ ⊢ τ2Φ ⊢ τ1 → τ2

wft-prodΦ ⊢ τ1Φ ⊢ τ2Φ ⊢ τ1 × τ2

Φ; Ψ ⊢ e : τ

wft-allΦ, α ⊢ τ2Φ ⊢ ∀α. τ2(Static semantics)

Ft-unitΦ⊢ΨΦ; Ψ ⊢ ⟨⟩ : ⟨⟩Ft-varΦ⊢Ψ(x : τ ) ∈ ΨΦ; Ψ ⊢ x : τ

Ft-intΦ⊢ΨΦ; Ψ ⊢ i : Int

Ft-absΦ; Ψ, x : τ1 ⊢ e : τ2
Φ ⊢ τ1
Φ; Ψ ⊢ λx. e : τ1 → τ2
Ft-tabs
Φ, α; Ψ ⊢ e : τΦ⊢ΨΦ; Ψ ⊢ Λα. e : ∀α. τ
Ft-pairΦ; Ψ ⊢ e1 : τ1
Φ; Ψ ⊢ e2 : τ2
Φ; Ψ ⊢ ⟨e1 , e2 ⟩ : τ1 × τ2

Ft-appΦ; Ψ ⊢ e1 : τ1 → τ2
Φ; Ψ ⊢ e2 : τ1
Φ; Ψ ⊢ e1 e2 : τ2
Ft-tappΦ; Ψ ⊢ e : ∀α. τ ′Φ⊢τΦ; Ψ ⊢ e τ : [τ /α]τ ′Ft-cappΦ; Ψ ⊢ e : τ1

co :: τ1 ▷ τ2
Φ; Ψ ⊢ co e : τ2

Φ ⊢ τ2

Figure 4.9: Typing rules of Fco

Elaboration. We go back to the translation parts in Fig. 4.6. The key idea of the translation remains the same: we translate merges to pairs. For disjoint quantification and disjoint
type applications (rules FT-tabs and FT-tapp), we translate them to regular universal quantification and type applications, respectively. To conclude, we show an example translation:(Λ(α ∗ Int). λx.x) : ∀(α ∗ Int). α & Int → α ⇝ (π1 → id)∀ (Λα. λx. x)

R+As with λ+i , we show two lemmas that relate Fi to Fco .

Lemma 4.8 (Coercions preserve types). If A <: B ⇝ co, then co :: |A| ▷ |B|.

Proof. By structural induction on the derivation of subtyping.

70

4.5 Algorithmic System and Decidabilitye −→ e′(Single-step reduction)

r-id

r-trans

r-top

r-topArr

id v −→ v(co1 ◦ co2 ) v −→ co1 (co2 v)

top v −→ ⟨⟩(top→ ⟨⟩) ⟨⟩ −→ ⟨⟩r-pairr-arr⟨co1 , co2 ⟩ v −→ ⟨co1 v, co2 v⟩((co1 → co2 ) v1 ) v2 −→ co2 (v1 (co1 v2 ))r-distArr

r-projlr-projr(dist→ ⟨v1 , v2 ⟩) v3 −→ ⟨v1 v3 , v2 v3 ⟩π1 ⟨v1 , v2 ⟩ −→ v1

π2 ⟨v1 , v2 ⟩ −→ v2

r-forallr-distAll

r-topAll(co∀ v) τ −→ co (v τ )(dist∀ ⟨v1 , v2 ⟩) τ −→ ⟨v1 τ, v2 τ ⟩(top∀ ⟨⟩) τ −→ ⟨⟩r-app

r-tapp(λx. e) v −→ [v/x]e(Λα. e) τ −→ [τ /α]eR

Lemma 4.9 (r-ctxte −→ e′E[e] −→ E[e′ ]Figure 4.10: Dynamic semantics of Fco

Elaboration soundness). We have that:• If ∆; Γ ⊢ E ⇒ A ⇝ e, then |∆|; |Γ| ⊢ e : |A|.
• If ∆; Γ ⊢ E ⇐ A ⇝ e, then |∆|; |Γ| ⊢ e : |A|.
Proof. By structural induction on the derivation of typing.

. Algorithmic System and Decidability
The subtyping relation in Fig. 4.5 is highly non-algorithmic due to the presence of a transitivity rule. This section presents an alternative algorithmic formulation. The algorithmic
subtyping extends that of λ+i , to handle disjoint quantifiers and the bottom type. We thenprove that the algorithm is sound and complete with respect to declarative subtyping.
Additionally we prove that the subtyping and disjointness relations are decidable. Although the proofs of this fact are fairly straightforward, it is nonetheless remarkable since
it contrasts with the subtyping relation for (full) F<: [Cardelli and Wegner 1985], which isundecidable [Pierce 1994]. Thus while bounded quantification is infamous for its undecidability, disjoint quantification has the nicer property of being decidable.

71

4 Semantics of the F+
i Calculus
Q ⊢ A ≺: B ⇝ co

(Algorithmic subtyping)A-and
Q ⊢ A ≺: B1 ⇝ co1

A-top
⊤

Q ⊢ A ≺: ⊤ ⇝ JQK ◦ top

A-constA-bot[] ⊢ c ≺: c ⇝ id

Q ⊢ ⊥ ≺: c ⇝ bot

A-rcdConst
Q ⊢ A ≺: c ⇝ col, Q ⊢ {l : A} ≺: c ⇝ co

Q ⊢ A ≺: B1 & B2 ⇝ JQK& ◦ ⟨co1 , co2 ⟩A-rcd
Q, l ⊢ A ≺: B ⇝ co
Q ⊢ A ≺: {l : B} ⇝ coA-arr
Q, B1 ⊢ A ≺: B2 ⇝ co
Q ⊢ A ≺: B1 → B2 ⇝ co

Q ⊢ A ≺: B2 ⇝ co2

A-forallQ, α ∗ B1 ⊢ A ≺: B2 ⇝ co
Q ⊢ A ≺: ∀(α ∗ B1 ). B2 ⇝ co

A-arrConst[] ⊢ A ≺: A1 ⇝ co1
Q ⊢ A2 ≺: c ⇝ co2
A, Q ⊢ A1 → A2 ≺: c ⇝ co1 → co2

A-andConst
Q ⊢ A1 ≺: c ⇝ co
Q ⊢ A1 & A2 ≺: c ⇝ co ◦ π1

A-andConst
Q ⊢ A2 ≺: c ⇝ co
Q ⊢ A1 & A2 ≺: c ⇝ co ◦ π2

A-allConst[] ⊢ A ≺: A1
Q ⊢ A2 ≺: c ⇝ co(α ∗ A, Q) ⊢ ∀(α ∗ A1 ). A2 ≺: c ⇝ co∀

Figure 4.11: Algorithmic subtyping of F+i.. Algorithmic Subtyping Rules
Following λ+i , we intend the algorithmic subtyping judgment Q ⊢ A ≺: B to be equivalent to
A <: Q ⇒ B, where Q is a queue used to track record labels, domain types and disjointness
constraints. The syntax of Q is shown below
Q ::= [] | l, Q | B, Q | α ∗ B, Q
The full rules of the algorithmic subtyping of F+
i are shown Fig. 4.11.
Definition 8. Q ⇒ A is inductively defined as follows:[] ⇒ A=A(B, Q) ⇒ A=

B → (Q ⇒ A)

(l, Q) ⇒ A={l : Q ⇒ A}

(α ∗ B, Q) ⇒ A=∀(α ∗ B). Q ⇒ A

For brevity of the algorithm, we use metavariable c to mean type constants:
Type Constants c ::= Int | ⊥ | α72

4.5 Algorithmic System and Decidability

J[]K⊤ = top

J[]K& = id

Jl, QK⊤ = JQK⊤ ◦ id

Jl, QK& = JQK& ◦ id

JA, QK⊤ = (top → JQK⊤ ) ◦ top→

JA, QK& = (id → JQK& ) ◦ dist→Jα ∗ A, QK⊤ = JQK⊤∀ ◦ top∀Jα ∗ A, QK& = JQK&
∀ ◦ dist∀

Figure 4.12: Meta-functions of coercions, extended

The basic idea of Q ⊢ A ≺: B is to perform a case analysis on B until it reaches type constants. We explain new rules regarding disjoint quantification and the bottom type. Whena quantifier is encountered in B, rule A-forall pushes the type variables with its disjointness constraints onto Q and continue with the body. Correspondingly, in rule A-allConst,
when a quantifier is encountered in A, and the head of Q is a type variable, this variable ispopped out and we continue with the body. Rule A-bot is similar to its declarative counterpart. Two meta-functions JQK⊤ and JQK& are meant to generate correct forms of coercions,and their definitions are shown in Fig. 4.12.
Correctness of the algorithm. We prove that the algorithm is sound and completewith respect to the specification. We refer the reader to our Coq formalization for moreRR

details. We only show the two major theorems:
Theorem 4.10 (Soundness). If Q ⊢ A ≺: B ⇝ co then A <: Q ⇒ B ⇝ co.

Theorem 4.11 (Completeness). If A <: B ⇝ co then there exists co′ such that [] ⊢ A ≺:B ⇝ co′ .

.. Decidability
Moreover, we prove that our algorithmic type system is decidable. To see this, first noticethat the bidirectional type system is syntax-directed, so we only need to show decidabilityof algorithmic subtyping and disjointness. The full (manual) proofs for decidability can befound in Appendix B.
Lemma 4.12 (Decidability of algorithmic subtyping). Given Q, A and B, it is decidable whether
there exists co, such that Q ⊢ A ≺: B ⇝ co.
Proof. Let the judgment Q ⊢ A ≺: B ⇝ co be measured by size(Q) + size(A) + size(B).
For each subtyping rule, we can show that every inductive premise is smaller than the conclusion.

73

4 Semantics of the F+
i Calculus
Lemma 4.13 (Decidability of disjointness checking). Given ∆, A and B, it is decidable whether∆ ⊢ A ∗ B.
Proof. Let the judgment ∆ ⊢ A ∗ B be measured by size(A) + size(B). For each subtyping
rule, we can show that every inductive premise is smaller than the conclusion.
One interesting observation here is that although our disjointness quantification has a similar shape to bounded quantification ∀(α <: A). B in F<: [Cardelli and Wegner 1985], subtyping for F<: is undecidable [Pierce 1994]. In F<: , the subtyping relation between bounded
quantification is:fsub-forall∆ ⊢ A2 <: A1∆, α <: A2 ⊢ B1 <: B2∆ ⊢ ∀(α <: A1 ). B1 <: ∀(α <: A2 ). B2
Compared with rule S-forall, both rules are contravariant on bounded/disjoint types, andcovariant on the body. However, with bounded quantification it is fundamental to track thebounds in the environment, which complicates the design of the rules and makes subtyping
undecidable with rule fsub-forall. Decidability can be recovered by employing an invariant rule for bounded quantification (that is by forcing A1 and A2 to be identical). Disjoint
quantification does not require such invariant rule for decidability.

74

Part III
Coherence75 Coherence for λ+iThis chapter constructs a logical relation to establish coherence of λ+i . Finding a suitabledefinition of coherence for λ+i is already challenging in its own right. In what follows we
reproduce the steps of finding a definition for coherence that is both intuitive and applicable. Then we present the construction of the logical (equivalence) relation tailored to thisdefinition, and the connection between logical equivalence and coherence. Chapter 6 builds
on the idea in this chapter to prove coherence for F+i .

. The Intuition
Duplication is harmless.

While requiring that all intersections are disjoint as in λi issufficient to guarantee coherence, it is not necessary. In fact, such requirement unnecessarily encumbers the subtyping definition with disjointness constraints and an ad-hoc treatment of “top-like” types. Indeed, the value 1 , , 1 of the non-disjoint type Int & Int is entirely
unambiguous, and (1 , , 1) + 3 can obviously only result in 4. More generally, when theoverlapping components of an intersection type have the same value, there is no ambiguity
problem. λ+i uses this idea to relax λi ’s enforcement of disjointness. In the case of a merge, it
is hard to statically decide whether the two arguments have the same value, and thus λ+i stillrequires disjointness. Yet, disjointness is no longer required for the well-formedness of types
and overlapping intersections can be created implicitly through subtyping, which results induplicating values at run time. For instance, while 1 , , 1 is not expressible 1 : Int & Int creates the equivalent value implicitly. In short, duplication is harmless and subtyping only
generates duplicated values for non-disjoint types.
Two factors make establishing coherence for λ+i much more difficult: the relaxation of
disjointness and the adoption of the more expressive subtyping rules from the BCD system(for which λi lacks). These two factors mean that subtyping proofs are no longer unique andhence that there are multiple elaborations of the same source program. For instance, Int & Int
is a subtype of Int in two ways: by projection on either the first or second component. Hencethe fact that all elaborations yield the same result when evaluated has become a much more
subtle property that requires sophisticated reasoning. For instance, we can see that coherence77

5 Coherence for λ+iholds because at run time any value of type Int & Int has identical components, and thus bothprojections yield the same result.
For λ+
i in general, we show coherence by capturing the non-ambiguity invariant in a logicalrelation [Plotkin 1973; Statman 1985; Tait 1967] and showing that it is preserved by the operational semantics. In doing so, we remove the brittleness of the previous syntactic method
to prove coherence. This new proof method has several advantages. Firstly, with the new
proof method, several restrictions that were enforced by λi to enable the syntactic proof areremoved. For example, the aforementioned top-like types are not necessary; top-like types
are handled like all other types. Secondly, the new proof method is more powerful because
it is based on contextual equivalence rather than syntactic equality; it is more robust as thetype system is extended. Finally, the removal of the ad-hoc side-conditions makes adding
new extensions, such as support for BCD-style subtyping, easier. A complicating factor isthat not one, but two languages are involved: the source language and the target language. In
order to deal with the complexity of the elaboration semantics of λ+i , we employ binary logical relations that are heterogeneous, parameterized by two types; the fundamental property
is also reformulated to account for bidirectional type-checking. A caveat is that our logicalrelation does not hold for target programs and program contexts in general, but only for
those that are the image of a corresponding source program or program context. Thus wemust view everything through the lens of elaboration.

. In Search of CoherenceIn λi the definition of coherence is based on α-equivalence. More specifically, the coherence
property in λi states that any two target terms that a source expression elaborates into must
be exactly the same (up to α-equivalence). Unfortunately this syntactic notion of coherenceis very fragile with respect to extensions. For example, it is not obvious how to retain thisnotion of coherence when adding more subtyping rules such as those in Fig. 3.3.
If we permit ourselves to consider only the syntactic aspects of expressions, then very few
expressions can be considered equal. The syntactic view also conflicts with the intuition
that “the significance of an expression lies in its contribution to the outcome of a computation” [Harper 2016]. Drawing inspiration from a wide range of literature on contextual
equivalence [Morris Jr 1969], we seek a context-based notion of coherence. It is helpful toconsider several examples before presenting the formal definition of our new semanticallyfounded notion of coherence.
Example 1. The same λ+i expression 1 can be typed Int in many ways: for instance, by rule Tlit; by rules T-sub and S-refl; or by rules T-sub, S-trans, and S-refl, resulting in transla-

785.2 In Search of Coherence

λco contexts
λ+i contexts

DC[·] | λx. D | D e | e D | ⟨D, e⟩ | ⟨e, D⟩ | co D[·] | λx. C | C E | E C | E , , C | C , , E | C : A{l = C} | C.l

::=
::=
|

Figure 5.1: Expression contexts of λco and λ+itions 1, id 1 and (id ◦ id) 1, respectively. It is apparent that these three λco terms are “equal”
in the sense that all reduce to the same numeral 1.

.. Expression Contexts and Contextual Equivalence.
To formalize the intuition, we turn to expression contexts, as introduced in Section 2.5. The
syntax of λco contexts D can be found in Fig. 5.1. The static semantics of λco is extended toexpression contexts by defining the typing judgment
D : (Ψ ⊢ τ ) 7→ (Ψ′ ⊢ τ ′ )where (Ψ ⊢ τ ) indicates the type of the hole. This judgment is inductively defined such thatif Ψ ⊢ e : τ , then Ψ′ ⊢ D{e} : τ ′ .
We define a complete program to mean any closed term of type Int. Recall the definitionsof Kleene equality and contextual equivalence in Section 2.5. For ease of reference, we restatethem below:
Definition 3 (Kleene Equality ⋍). Two complete programs, e and e′ , are Kleene equal, written e ⋍ e′ , if there exists i such that e −→∗ i and e′ −→∗ i.
Definition 4 (Contextual Equivalence ⋍ctx ).
Ψ ⊢ e1 ⋍ctx e2 : τ ≜ Ψ ⊢ e1 : τ ∧ Ψ ⊢ e2 : τ ∧(∀D. D : (Ψ ⊢ τ ) 7→ (• ⊢ Int) =⇒ D{e1 } ⋍ D{e2 })Regarding Example 1, it seems adequate to say that 3 and id 3 are contextually equivalent.
Does this imply that coherence can be based on Definition 4? Unfortunately it cannot, as
demonstrated by the following example.
Example 2. It may be counter-intuitive that two λco terms λx. π1 x and λx. π2 x should
also be considered equal. To see why, first note that they are both the translations of thesame λ+i expression: (λx. x) : Int & Int → Int. What can we do with this lambda abstraction? We can apply it to 1 for example, which leads to two translations (λx. π1 x) ⟨1, 1⟩ and79

5 Coherence for λ+i(λx. π2 x) ⟨1, 1⟩. It is obvious that both reduce to the same numeral 1. However, λx. π1 x and
λx. π2 x are definitely not equal according to Definition 4, as one can find a context [·] ⟨1, 2⟩
in which the two terms reduce to two different numerals. The problem is that [·] ⟨1, 2⟩ should
not be considered because the (non-disjoint) source expression 1 , , 2 is rejected by the typesystem of the source calculus λ+i and thus never gets elaborated into ⟨1, 2⟩.

.. λ+i Contexts and Refined Contextual Equivalence.
Example 2 hints at a shift from λco contexts to λ+i contexts C, whose syntax is shown in
Fig. 5.1. Due to the bidirectional nature of the type system, the typing judgment of C features
4 different forms:
C : (Γ ⇒ A) 7→ (Γ′ ⇒ A′ ) ⇝ D

C : (Γ ⇐ A) 7→ (Γ′ ⇒ A′ ) ⇝ D

C : (Γ ⇒ A) 7→ (Γ′ ⇐ A′ ) ⇝ D

C : (Γ ⇐ A) 7→ (Γ′ ⇐ A′ ) ⇝ D

We write C : (Γ ⇔ A) 7→ (Γ′ ⇔′ A′ ) ⇝ D to abbreviate the above 4 different forms. Take
C : (Γ ⇒ A) 7→ (Γ′ ⇒ A′ ) ⇝ D for example (whose typing rules are shown in Fig. 5.2),
it reads that if Γ ⊢ E ⇒ A, then Γ′ ⊢ C{E} ⇒ A′ . The judgment also generates a welltyped λco context D such that D : (|Γ| ⊢ |A|) 7→ (|Γ′ | ⊢ |A′ |) holds by construction. The
full typing rules appear in Appendix D. Now we are ready to refine Definition 4’s contextual
equivalence to take into consideration both λ+i and λco contexts.
Definition 9 (λ+i Contextual Equivalence).
Γ ⊢ E1 ⋍ctx E2 : A ≜ ∀e1 , e2 . Γ ⊢ E1 ⇒ A ⇝ e1 ∧ Γ ⊢ E2 ⇒ A ⇝ e2 ∧(∀C, D. C : (Γ ⇒ A) 7→ (• ⇒ Int) ⇝ D =⇒ D{e1 } ⋍ D{e2 })In other words, two source expressions are contextually equivalent if their translations
are equivalent in all possible source contexts. For brevity we only consider expressions in
the inference mode. Our Coq formalization is complete with two modes. Now regarding
Example 2, a possible λ+i context is[·] 1 : (• ⇒ Int & Int → Int) 7→ (• ⇒ Int) ⇝ [·] ⟨1, 1⟩
We can verify that both λx. π1 x and λx. π2 x produce 1 in the context [·] ⟨1, 1⟩. Of course weshould consider all possible contexts to be certain that they are truly equal. From now on,
we use the symbol ⋍ctx to refer to contextual equivalence in Definition 9. With Definition 9
we can formally state that λ+i is coherent in the following sense:805.3 The Canonicity Relation, Formally Defined
C : (Γ ⇒ A) 7→ (Γ′ ⇒ B) ⇝ D(Context typing I)
CTyp-appL
C : (Γ ⇒ A) 7→ (Γ′ ⇒ A1 → A2 ) ⇝ D
Γ′ ⊢ E2 ⇐ A1 ⇝ e
C E2 : (Γ ⇒ A) 7→ (Γ′ ⇒ A2 ) ⇝ D e

CTyp-empty
[·] : (Γ ⇒ A) 7→ (Γ ⇒ A) ⇝ [·]CTyp-appRΓ′ ⊢ E1 ⇒ A1 → A2 ⇝ e
C : (Γ ⇒ A) 7→ (Γ′ ⇐ A1 ) ⇝ D
E1 C : (Γ ⇒ A) 7→ (Γ′ ⇒ A2 ) ⇝ e D

CTyp-mergeL
C : (Γ ⇒ A) 7→ (Γ′ ⇒ A1 ) ⇝ D
Γ′ ⊢ E2 ⇒ A2 ⇝ e
A1 ∗ A2′
C , , E2 : (Γ ⇒ A) 7→ (Γ ⇒ A1 & A2 ) ⇝ ⟨D, e⟩

CTyp-mergeRΓ′ ⊢ E1 ⇒ A1 ⇝ e
C : (Γ ⇒ A) 7→ (Γ′ ⇒ A2 ) ⇝ D
A1 ∗ A2′
E1 , , C : (Γ ⇒ A) 7→ (Γ ⇒ A1 & A2 ) ⇝ ⟨e, D⟩
CTyp-rcdC : (Γ ⇒ A) 7→ (Γ′ ⇒ B) ⇝ D{l = C} : (Γ ⇒ A) 7→ (Γ′ ⇒ {l : B}) ⇝ D

R

Theorem 5.1 (CTyp-anno
C : (Γ ⇒ B) 7→ (Γ′ ⇐ A) ⇝ D
C : A : (Γ ⇒ B) 7→ (Γ′ ⇒ A) ⇝ D
CTyp-projC : (Γ ⇒ A) 7→ (Γ′ ⇒ {l : B}) ⇝ D
C.l : (Γ ⇒ A) 7→ (Γ′ ⇒ B) ⇝ D

Figure 5.2: λ+i context typing (excerpt)

Coherence). We have that

• If Γ ⊢ E ⇒ A then Γ ⊢ E ⋍ctx E : A.
• If Γ ⊢ E ⇐ A then Γ ⊢ E ⋍ctx E : A.
That is, coherence is a special case of Definition 9 where E1 and E2 are the same. At firstglance, this appears underwhelming: of course E behaves the same as itself! The tricky partis that, if we expand it according to Definition 9, it is not E itself but all its translations e1
and e2 that behave the same! The rest of the chapter is devoted to proving the validity ofTheorem 5.1.

. The Canonicity Relation, Formally Defined
As intuitive as Definition 9 may seem, it is generally very hard to prove contextual equivalence
directly, since it involves quantification over all possible contexts. Worse still, two kinds ofcontexts are involved in Theorem 5.1, which makes reasoning even more tedious. The key
to simplifying the reasoning is to exploit types using logical relations [Plotkin 1973; Statman
1985; Tait 1967].

81

5 Coherence for λ+i(v1 , v2 ) ∈ VJInt; IntK
(v1 , v2 ) ∈ VJ{l : A}; {l : B}K(v1 , v2 ) ∈ VJA1 → B1 ; A2 → B2 K(⟨v1 , v2 ⟩, v3 ) ∈ VJA & B; CK(v3 , ⟨v1 , v2 ⟩) ∈ VJC; A & BK
(v1 , v2 ) ∈ VJA; BK

≜
≜
≜
≜
≜
≜

∃i. v1 = v2 = i(v1 , v2 ) ∈ VJA; BK∀(v′2 , v′1 ) ∈ VJA2 ; A1 K. (v1 v′1 , v2 v′2 ) ∈ EJB1 ; B2 K
(v1 , v3 ) ∈ VJA; CK ∧ (v2 , v3 ) ∈ VJB; CK(v3 , v1 ) ∈ VJC; AK ∧ (v3 , v2 ) ∈ VJC; BK
true otherwise(e1 , e2 ) ∈ EJA; BK≜∃v1 , v2 . e1 −→∗ v1 ∧ e2 −→∗ v2 ∧ (v1 , v2 ) ∈ VJA; BK

Figure 5.3: The canonicity relation for λ+i

In search of a logical relation. It is worth pausing to ponder what kind of relation
we are looking for. The high-level intuition behind the relation is to capture the notion of“coherent” values. These values are unambiguous in all possible (source) contexts. A momentof thought leads us to the following observations:Observation 1 (Disjoint values are unambiguous). The relation should relate values whosetypes are disjoint. Those values are essentially translated from merges, and since rule Tmerge ensures disjointness, they are unambiguous. For example, one value of type Int andthe other of {l : Int} can be unambiguously distinguished by any source context.
Observation 2 (Duplication is unambiguous). The relation should also relate values originating from non-disjoint intersection types, only if the values are duplicates. This may soundbaffling, since the whole point of disjointness is to rule out (ambiguous) expressions such as
1 , , 2. However, 1 , , 2 never gets elaborated, and the only values corresponding to Int & Int
are those pairs such as ⟨1, 1⟩, ⟨2, 2⟩, etc. Those values are essentially generated from rule Tsub by subtyping and are also unambiguous.
The canonicity relation. In order to deal with the complexity of the elaboration semantics, we introduce in Fig. 5.3 what we call the canonicity relation to capture “canonical”
values based on the above observations.1 The canonicity relation is a family of binary relations over λco values that are heterogeneous, i.e., indexed by two λ+i types. Heterogeneity
allows us to relate values of different types, and in particular values whose types are disjoint.
The canonicity relation seeks to combine equality checking from traditional (homogeneous)
logical relations (Observation 2) with disjointness checking (Observation 1). It consists of
two relations. The value relation VJA; BK relates closed values, i.e., well-typed values with
1

The logical relation is slightly different from that in the original publication [Bi et al. 2018] in that it is indexed
by “source” types whereas in the publication it is indexed by “target” types. For λ+i , both formulations work
equally fine. The choice here is mainly for consistency reasons as the logical relation for F+
i must be indexedby source types.

82

5.3 The Canonicity Relation, Formally Definedno free variables. Similarly, the expression relation EJA; BK relates closed expressions. For
brevity, we write VJAK to mean VJA; AK, and EJAK for EJA; AK.
First let us consider the relation VJA; BK, which specifies when two closed values v1 and v2
are related at the types A and B. The definition for integers and records are straightforward.
Two integers are related if they are equal. For records, recall that in Section 3.4, record labels
are erased during translation. Therefore two values are related at two record types of thesame label if they are related at the two field types.
Functions v1 and v2 are related at the types A1 → B1 and A2 → B2 if given two arguments′
v1 and v′2 related at the argument types A1 and A2 , the functions applied to the arguments
are related expressions at the result types B1 and B2 . Note that in λco , the values v1 and v2
may each be a lambda abstraction, or a coercion application of a function type.
The definition of VJA; BK is made more interesting when one of the indexed types is anintersection type. In that case, the relation distributes over the type constructor & . It is
instructive to compare the type constructor & with product types ×. The traditional wayof relating pairs is by relating their components pairwise. That is, ⟨v1 , v2 ⟩ and ⟨v′1 , v′2 ⟩ arerelated at A × B if (1) v1 and v′1 are related at A and (2) v2 and v′2 are related at B. According

to our definition, we also require that (3) v1 and v′2 are related and (4) v2 and v′1 are related.

To see why this is the case, consider whether (⟨1, 2⟩, ⟨1, 2⟩) ∈ VJInt & IntK. If we regard
Int & Int as a normal product type, then these two pairs are related. However, as remarked
earlier, ⟨1, 2⟩ should not be considered as the image of some source expression at the type
Int & Int, and our definition correctly rejects it because 1 is not equal to 2, while accepting
pairs such as ⟨1, 1⟩, ⟨2, 2⟩, etc.
The acute reader may have noticed the structural similarity between the two clauses forintersection types and the disjointness rules for intersection types:
D-andR

D-andLA1 ∗ BA2 ∗ B

A1 & A2 ∗ B

A ∗ B1A ∗ B2A ∗ B1 & B2

This is not a coincidence—we can show that disjointness and the value relation are connectedRby the following lemma:
Lemma 5.2 (VJA; BK.

Disjoint values are related). If A∗B and v1 : |A| and v2 : |B|, then (v1 , v2 ) ∈Proof. By induction on the derivation of disjointness.
Next we consider EJA; BK, which is standard. Informally it expresses that two closed terms
e1 and e2 are related if they evaluate to two values v1 and v2 that are related.

83

5 Coherence for λ+i
Logical equivalence. The logical relation can be lifted to open terms in the usual way.
First we give the semantic interpretation of typing contexts. A closing substitution γ forthe typing context Γ = x1 : A1 , . . . , xn : An is a finite function assigning closed values v1 : |A1 |, . . . , vn : |An | to x1 , . . . , xn , respectively. We write γ(e) for the substitution[v1 , . . . , vn /x1 , . . . , xn ]e. The interruption of typing contexts, written (γ1 , γ2 ) ∈ GJΓK is

R

inductively defined as follows:
Definition 10 (Interpretation of value contexts).
(γ1 , γ2 ) ∈ GJΓK(∅, ∅) ∈ GJ•K

(v1 , v2 ) ∈ VJAK

(γ1 [x 7→ v1 ], γ2 [x 7→ v2 ]) ∈ GJΓ, x : AKThe canonicity relation can be lifted to open expressions in the standard way, i.e., two openR

terms are related if every pair of related closing substitutions makes them related:
Definition 11 (Logical equivalence).

Γ ⊢ e1 ⋍log e2 : A; B ≜ |Γ| ⊢ e1 : |A| ∧ |Γ| ⊢ e2 : |B| ∧(∀γ1 , γ2 . (γ1 , γ2 ) ∈ GJΓK =⇒ (γ1 (e1 ), γ2 (e2 )) ∈ EJA; BK)
For succinctness, we write Γ ⊢ e1 ⋍log e2 : A to mean Γ ⊢ e1 ⋍log e2 : A; A.

. Establishing CoherenceWith all the machinery in place, we are now ready to prove Theorem 5.1. But we need several
lemmas to set the stage.
Firstly we need the compatibility lemmas, which state that logical equivalence is preservedby language constructs. Most of them are standard and are thus omitted. We show only twoRcompatibility lemmas that are specific to our logical relation:
Lemma 5.3 (Coercion compatibility). Suppose that A1 <: A2 ⇝ co,• If Γ ⊢ e1 ⋍log e2 : A1 ; A0 then Γ ⊢ co e1 ⋍log e2 : A2 ; A0 .
• If Γ ⊢ e1 ⋍log e2 : A0 ; A1 then Γ ⊢ e1 ⋍log co e2 : A0 ; A2 .

R

Proof. By induction on the subtyping derivation.
Lemma 5.4 (Merge compatibility). If Γ ⊢ e1 ⋍log e′1 : A, Γ ⊢ e2 ⋍log e′2 : B and A ∗ B,

then Γ ⊢ ⟨e1 , e2 ⟩ ⋍log ⟨e′1 , e′2 ⟩ : A & B.

84

5.4 Establishing Coherence
Proof. By the definition of logical relation and Lemma 5.2.
The “Fundamental Property” states that any well-typed expression is related to itself by
the logical relation. In our elaboration setting, we rephrase it so that any two λco terms
elaborated from the same λ+i expression are related by the logical relation. To prove it, weRR

require Theorem 5.5.
Theorem 5.5 (A1 ≡α A2 .

Theorem 5.6 (Inference uniqueness). If Γ ⊢ E ⇒ A1 and Γ ⊢ E ⇒ A2 , thenFundamental property). We have that:• If Γ ⊢ E ⇒ A ⇝ e and Γ ⊢ E ⇒ A ⇝ e′ , then Γ ⊢ e ⋍log e′ : A.
• If Γ ⊢ E ⇐ A ⇝ e and Γ ⊢ E ⇐ A ⇝ e′ , then Γ ⊢ e ⋍log e′ : A.
Proof. The proof follows by induction on the first derivation. The most interesting case isrule T-sub
T-subΓ⊢E⇒A⇝e

A <: B ⇝ co

Γ ⊢ E ⇐ B ⇝ co e
where we need Theorem 5.5 to be able to apply the induction hypothesis. Then we apply
Lemma 5.3 to say that the coercion generated preserves the relation between terms. For theother cases we use the appropriate compatibility lemmas.
We show that logical equivalence is preserved by λ+i contexts:
Lemma 5.7 (R

Congruence). If C : (Γ ⇔ A) 7→ (Γ′ ⇔′ A′ ) ⇝ D, Γ ⊢ E1 ⇔ A ⇝ e1 ,Γ ⊢ E2 ⇔ A ⇝ e2 and Γ ⊢ e1 ⋍log e2 : A, then Γ′ ⊢ D{e1 } ⋍log D{e2 } : A′ .
Proof. By induction on the typing derivation of the context C, and applying the compatibilityRlemmas where appropriate.
Lemma 5.8 (Adequacy). If • ⊢ e1 ⋍log e2 : Int then e1 ⋍ e2 .

Proof. Adequacy follows easily from the definition of the logical relation.
Next up is the proof that logical relation is sound with respect to contextual equivalence—that is, if two programs are logically related then they are contextually equivalent—which
justifies the use of logical relation for proving contextual equivalence of programs.

85

5 Coherence for λ+i
Theorem 5.9 (Soundness w.r.t. contextual equivalence). Given Γ ⊢ e1 ⋍log e2 : A, we have
• If Γ ⊢ E1 ⇒ A ⇝ e1 and Γ ⊢ E2 ⇒ A ⇝ e2 then Γ ⊢ E1 ⋍ctx E2 : A.
• If Γ ⊢ E1 ⇐ A ⇝ e1 and Γ ⊢ E2 ⇐ A ⇝ e2 then Γ ⊢ E1 ⋍ctx E2 : A.
Proof. From Definition 9, we are given a context C : (Γ ⇒ A) 7→ (• ⇒ Int) ⇝ D. By
Lemma 5.7 we have • ⊢ D{e1 } ⋍log D{e2 } : Int, thus D{e1 } ⋍ D{e2 } by Lemma 5.8.

R

Armed with Theorem 5.6 and Theorem 5.9, coherence follows directly.
Theorem 5.1 (Coherence). We have that

• If Γ ⊢ E ⇒ A then Γ ⊢ E ⋍ctx E : A.
• If Γ ⊢ E ⇐ A then Γ ⊢ E ⋍ctx E : A.
Proof. Immediate from Theorem 5.6 and Theorem 5.9.

. Some Interesting Corollaries
To showcase the strength of the new proof method, we can derive some interesting corollaries. For the most part, they are direct consequences of logical equivalence which carry overto contextual equivalence.
Corollary 5.10 says that merging an expression E1 of some type with an arbitrary expression E2 does not affect the semantics of E1 at the same type. Corollary 5.11 and Corollary 5.12 express that merges are commutative and associative, respectively. Corollary 5.13
states that coercions from the same types are “coherent”, i.e., they can be used interchangeably.
Corollary 5.10 (Neutrality). If Γ ⊢ E1 ⇒ A and Γ ⊢ E1 , , E2 ⇒ A, then Γ ⊢ E1 ⋍ctx
E1 , , E2 : A
Corollary 5.11 (Commutativity). If Γ ⊢ E1 , , E2 ⇒ A and Γ ⊢ E2 , , E1 ⇒ A, thenΓ ⊢ E1 , , E2 ⋍ctx E2 , , E1 : A.
Corollary 5.12 (Associativity). If Γ ⊢ (E1 , , E2 ) , , E3 ⇒ A and Γ ⊢ E1 , , (E2 , , E3 ) ⇒A, then Γ ⊢ (E1 , , E2 ) , , E3 ⋍ctx E1 , , (E2 , , E3 ) : A.
Corollary 5.13 (Coercions Preserve Semantics). If A <: B ⇝ co1 and A <: B ⇝ co2 , thenΓ ⊢ λx. co1 x ⋍log λx. co2 x : A → B.

86 Coherence for F+i

In this section, we establish the coherence property for F+
i . The proof strategy mostly follows that of λ+i , but the construction of the heterogeneous logical relation is significantly
more complicated. Firstly in Section 6.1 we discuss why adding BCD subtyping to disjoint
polymorphism introduces significant complications. In Section 6.2, we discuss why a naturalextension of System F’s logical relation to deal with disjoint polymorphism fails. The technical difficulty is well-foundedness, stemming from the interaction between impredicativity
and disjointness. Finally in Section 6.3, we present our (predicative) logical relation that is
specially crafted to prove coherence for F+i .

. The Challenge
Before we tackle the coherence of F+i , let us first consider how Fi (and its predecessor λi ) enforces coherence. Its essentially syntactic approach is to make sure that there is at most one
subtyping derivation for any two types. As an immediate consequence, the produced coercions are uniquely determined and thus the calculus is clearly coherent. Key to this approachis the invariant that the type system only produces disjoint intersection types. As we mentioned in Section 4.2, this invariant complicates the calculus and its metatheory, and leads toa weaker substitution lemma. Moreover, the syntactic coherence approach is incompatible
with BCD subtyping, which leads to multiple subtyping derivations with different coercions
and requires a more general substitution lemma. For example, consider the coercions produced by ∀(α ∗ Int). α & α <: ∀(α ∗ Int & Int). α (neither type is “well-formed” in the senseof Fi ). Two possible ones are λf. Λα. π1 (f α) and λf. Λα. π2 (f α). It is not at all obviousthat they should be equivalent in an appropriate sense. As we have shown in Chapter 5, to
prove coherence for λ+i , we need a semantically-founded proof method based on logical relations. Because λ+i does not feature polymorphism, the problem at hand is to incorporate
support for polymorphism in this semantic approach to coherence, which turns out to be
more challenging than is apparent.

87

6 Coherence for F+i. Impredicativity and Disjointness at Odds
Well-foundedness issues. For F+
i , we need to extend canonicity with additional cases
to account for universally quantified types. A naive formulation of one case rule is:(v1 , v2 ) ∈ VJ∀(α ∗ A1 ). B1 ; ∀(α ∗ A2 ). B2 K ≜∀C1 ∗ A1 , C2 ∗ A2 . (v1 |C1 |, v2 |C2 |) ∈ EJ[C1 /α]B1 ; [C2 /α]B2 K
This case is problematic because it destroys the well-foundedness of λ+i ’s logical relation,
which is based on structural induction on the type indices. Indeed, the type [C1 /α]B1 may
well be larger than ∀(α ∗ A1 ). B1 .
However, System F’s well-known parametricity logical relation [Reynolds 1983] provides
us with a means to avoid this problem. Rather than performing the type substitution immediately as in the above rule, we can defer it to a later point by adding it to an extra parameterρ of the relation, which accumulates the deferred substitutions. This yields a modified rule
where the type indices in the recursive occurrences are indeed smaller:(v1 , v2 ) ∈ VJ∀(α ∗ A1 ). B1 ; ∀(α ∗ A2 ). B2 Kρ ≜∀C1 ∗ A1 , C2 ∗ A2 .(v1 |C1 |, v2 |C2 |) ∈ EJB1 ; B2 Kρ[α7→(C1 ,C2 )]Of course, the deferred substitution has to be performed eventually, to be precise when thetype indices are type variables.
(v1 , v2 ) ∈ VJα; αKρ ≜ (v1 , v2 ) ∈ VJρ1 (α); ρ2 (α)K∅
Unfortunately, this way we have not only moved the type substitution to the type variablecase, but also the ill-foundedness problem. Indeed, this problem is also present in System
F. The standard solution is to not fix the relation R by which values at type α are related toVJρ1 (α); ρ2 (α)K, but instead to make it a parameter that is tracked by ρ. This yields thefollowing two rules for disjoint quantification and type variables:(v1 , v2 ) ∈ VJ∀(α ∗ A1 ). B1 ; ∀(α ∗ A2 ). B2 Kρ ≜ ∀C1 ∗ A1 , C2 ∗ A2 , R ⊆ C1 × C2 .
(v1 |C1 |, v2 |C2 |) ∈ EJB1 ; B2 Kρ[α7→(C1 ,C2 ,R)]
(v1 , v2 ) ∈ VJα; αKρ ≜ (v1 , v2 ) ∈ ρR (α)
Now we have finally recovered the well-foundedness of the relation. It is again structurally
inductive on the size of the type indexes.

886.3 The Canonicity Relation for F+i
Heterogeneous issues. We have not yet accounted for one major difference between theparametricity relation, from which we have borrowed ideas, and the canonicity relation, towhich we have been adding. The former is homogeneous (i.e., the types of the two values
is the same) and therefore has one type index, while the latter is heterogeneous (i.e., thetwo values may have different types) and therefore has two type indices. Thus we must also
consider cases like VJα; IntK. A definition that seems to handle this case appropriately is:(v1 , v2 ) ∈ VJα; IntKρ ≜ (v1 , v2 ) ∈ VJρ1 (α); IntK∅(6.1)

Here is an example to motivate this case. Let E = Λ(α∗⊤). (λx. x) : α & Int → α & Int. We
expect that E Int 1 −→∗ ⟨1, 1⟩, which boils down to showing (1, 1) ∈ VJα; IntK[α7→(Int,Int,R)] .
According to Eq. (6.1), this is indeed the case. However, we run into ill-foundedness issue
again, because ρ1 (α) could be larger than α. Alas, this time the parametricity relation has
no solution for us.

. The Canonicity Relation for F+i
In light of the fact that substitution in the logical relation seems unavoidable in our setting,and that impredicativity is at odds with substitution, we turn to predicativity: we change
rule FT-tapp to its predicative version:
FT-tappMono∆; Γ ⊢ E ⇒ ∀(α ∗ B). C ⇝ e∆⊢t∗B∆; Γ ⊢ E t ⇒ [t/α]C ⇝ e |t|where metavariable t ranges over monotypes, whose syntax is shown below
Monotypes t ::= Int | ⊤ | t1 → t2 | t1 & t2 | {l : t} | α
We do not believe that predicativity is a severe restriction in practice, since many source
languages (e.g., those based on the Hindley-Milner type system [Hindley 1969; Milner 1978]
like Haskell and OCaml) are themselves predicative and do not require the full generality ofan impredicative core language.
Luckily because monotypes do not contain ∀-quantifiers, substitution with monotypes
preserves well-foundedness. Figure 6.1 defines the canonicity relation for F+
i . The canonicity
relation is a family of binary relations over Fco values that are heterogeneous, i.e., indexed by
two F+i types. It consists of two relations: the value relation VJA; BK relates closed values;89

6 Coherence for F+i(v1 , v2 ) ∈ VJInt; IntK
(v1 , v2 ) ∈ VJ{l : A}; {l : B}K(v1 , v2 ) ∈ VJA1 → B1 ; A2 → B2 K(⟨v1 , v2 ⟩, v3 ) ∈ VJA & B; CK(v3 , ⟨v1 , v2 ⟩) ∈ VJC; A & BK
(v1 , v2 ) ∈ VJ∀(α ∗ A1 ). B1 ; ∀(α ∗ A2 ). B2 K
(v1 , v2 ) ∈ VJA; BK

≜
≜
≜
≜
≜
≜
≜

∃i. v1 = v2 = i(v1 , v2 ) ∈ VJA; BK∀(v′2 , v′1 ) ∈ VJA2 ; A1 K. (v1 v′1 , v2 v′2 ) ∈ EJB1 ; B2 K
(v1 , v3 ) ∈ VJA; CK ∧ (v2 , v3 ) ∈ VJB; CK(v3 , v1 ) ∈ VJC; AK ∧ (v3 , v2 ) ∈ VJC; BK∀• ⊢ t ∗ A1 & A2 . (v1 |t|, v2 |t|) ∈ EJ[t/α]B1 ; [t/α]B2 K
true otherwise(e1 , e2 ) ∈ EJA; BK≜∃v1 , v2 . e1 −→∗ v1 ∧ e2 −→∗ v2 ∧ (v1 , v2 ) ∈ VJA; BK

Figure 6.1: The canonicity relation for F+iand the expression relation EJA; BK—defined in terms of the value relation—relates closed
expressions.
The relation VJA; BK is defined by induction on the structures of A and B. For integers,
it requires the two values to be literally the same. For two records to behave the same, theirfields must behave the same. For two functions to behave the same, they are required to produce outputs related at B1 and B2 when given related inputs at A1 and A2 . For the next twocases regarding intersection types, the relation distributes over intersection constructor & .
Of particular interest is the case for disjoint quantification. Notice that it does not quantify
over arbitrary relations, but directly substitutes α with monotype t in B1 and B2 . This meansthat our canonicity relation does not entail parametricity [Reynolds 1983]. However, it suffices for our purposes to prove coherence. Another noticeable thing is that the value relation
VJA; BK (and EJA; BK) keeps the invariant that A and B are closed types. As a result, typevariables need not to be considered in the logical relation. This simplifies things a lot. Notethat when one type is ⊥, two values are vacuously related because there simply are no valuesof type ⊥. We need to show that the relation is indeed well-founded:
Lemma 6.1 (Well-foundedness). The canonicity relation of F+
i is well-founded.
Proof. Let | · |∀ and | · |s be the number of ∀-quantifies and the size of types, respectively. We
consider the measure ⟨| · |∀ , | · |s ⟩, where ⟨. . .⟩ denotes lexicographic order. For the case of
disjoint quantification, the number of ∀-quantifiers decreases because monotype t does notcontain ∀-quantifiers. For the other cases, the measure of | · |∀ does not increase, and themeasure of | · |s strictly decreases.

R

The logical relation is symmetric:
Lemma 6.2 (Symmetry). If (v1 , v2 ) ∈ VJA; BK then (v2 , v1 ) ∈ VJB; AK.

Proof. Symmetry of Fig. 5.3 is trivial. For Fig. 6.1, the proof proceeds by first induction on|A|∀ , then simultaneous induction on the structures of A and B.

90

6.4 Establishing CoherenceR

The interpretations of type and term contexts are given below:
Definition 12 (Interpretation of type contexts).
ρ ∈ DJ∆K∅ ∈ DJ•K

Definition 13 (R

• ⊢ t ∗ ρ(B)

ρ[α 7→ t] ∈ DJ∆, α ∗ BK

Interpretation of value contexts).
(γ1 , γ2 ) ∈ GJΓKρ(∅, ∅) ∈ GJ•Kρ

(v1 , v2 ) ∈ VJρ(A)K(γ1 [x 7→ v1 ], γ2 [x 7→ v2 ]) ∈ GJΓ, x : AKρ

The connection between disjointness and the logical relation becomes more complicatedR

due to the addition of polymorphism. We first show values of disjoint monotypes are related.
Lemma 6.3 (Disjoint values of monotypes are related). If • ⊢ t1 ∗ t2 , •; • ⊢ v1 : |t1 | and•; • ⊢ v2 : |t2 | then (v1 , v2 ) ∈ VJt1 ; t2 K.
Proof. By simultaneous induction on t1 and t2 .

R

Then we can prove a more general lemma.
Lemma 6.4 (Disjoint values are related). If ∆ ⊢ A ∗ B, ρ ∈ DJ∆K, •; • ⊢ v1 : |ρ(A)|and •; • ⊢ v2 : |ρ(B)| then (v1 , v2 ) ∈ VJρ(A); ρ(B)K.
Proof. By induction on the derivation of disjointness. The most interesting case is the variable rule:

FD-tvarL(α ∗ A) ∈ ∆A <: B∆⊢α∗B
By the definition of ρ, we know ρ(α) is a monotype. If B is a polytype, then it follows easily
from the definition of logical relation. If B is also a monotype, ρ(α) and ρ(A) are disjoint bydefinition. Then by Lemma 4.3 and A <: B, we have ρ(α) and ρ(B) are also disjoint. Finally
we apply Lemma 6.3.

. Establishing Coherence
We are now ready to prove coherence for F+
i . The proof of coherence basically follows thatin Chapter 5.

91

6 Coherence for F+i
Fco contextsF+i contexts

D
C

::=
::=
|

[·] | λx. D | Λα. D | D τ | D e | e D | ⟨D, e⟩ | ⟨e, D⟩ | co D[·] | λx. C | Λ(α ∗ A). C | C A | C E | E C | C , , E | E , , C{l = C} | C.l | C : A

Figure 6.2: Expression contexts of Fco and F+i

Logical equivalence. The canonicity relation can be lifted to open expressions in thestandard way, i.e., by considering all possible interpretations of free type and term variables.
Definition 14 (Logical equivalence ⋍log ).
∆; Γ ⊢ e1 ⋍log e2 : A; B ≜ |∆|; |Γ| ⊢ e1 : |A| ∧ |∆|; |Γ| ⊢ e2 : |B| ∧(∀ρ, γ1 , γ2 . ρ ∈ DJ∆K ∧ (γ1 , γ2 ) ∈ GJΓKρ =⇒ (γ1 (ρ1 (e1 )), γ2 (ρ2 (e2 ))) ∈ EJρ(A); ρ(B)K)
For conciseness, we write ∆; Γ ⊢ e1 ⋍log e2 : A to mean ∆; Γ ⊢ e1 ⋍log e2 : A; A.
Contextual equivalence. Following λ+i , the notion of coherence is based on contextual
equivalence. The intuition is that two programs are equivalent if we cannot tell them apartin any context. More formally, we introduce expression contexts, whose syntax is shown in
Fig. 6.2. Due to the bidirectional nature of the type system, the typing judgment of C features
4 different forms (see Appendix E), e.g., C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ A′ ) ⇝ D reads if∆; Γ ⊢ E ⇒ A then ∆′ ; Γ′ ⊢ C{E} ⇒ A′ . The judgment also generates a well-typed Fcocontext D. The following definition capture the notion of contextual equivalence in F+i :Definition 15 (F+i Contextual Equivalence).
∆; Γ ⊢ E1 ⋍ctx E2 : A ≜ ∀e1 , e2 . ∆; Γ ⊢ E1 ⇒ A ⇝ e1 ∧ ∆; Γ ⊢ E2 ⇒ A ⇝ e2 ∧(∀C, D. C : (∆; Γ ⇒ A) 7→ (•; • ⇒ Int) ⇝ D =⇒ D{e1 } ⋍ D{e2 })Coherence. We directly show the coherence statement of F+
i . We need several technicallemmas such as compatibility lemmas, fundamental property, etc. The interested reader canRrefer to our Coq formalization.
Theorem 6.5 (Coherence of F+i ). We have that

• If ∆; Γ ⊢ E ⇒ A then ∆; Γ ⊢ E ⋍ctx E : A.
• If ∆; Γ ⊢ E ⇐ A then ∆; Γ ⊢ E ⋍ctx E : A.

92

Part IV
Applications

93 First-Class Traits
In this chapter and Chapter 8, we present two applications of F+
i . This chapter is primarily
concerned with building a source-level language called SEDEL that features typed first-classtraits, dynamic inheritance and nested composition among others. We show how to model
source-level constructs for first-class traits and dynamic inheritance, supporting standard
object-oriented features such as dynamic dispatching and abstract methods. It is remarkablethat all of these can be explained by plain F+
i expressions, showing its expressive power. In
Chapter 8 we conduct a case study that modularizes programming language features by themeans of first-class traits.

. Motivation: First-Class Classes and Dynamic Inheritance
Many dynamically typed languages (including JavaScript, Ruby, Python or Racket) support
first-class classes [Flatt et al. 2006], or related concepts such as first-class mixins and/or traits.
In those languages classes are first-class values and, like any other values, they can be passed
as an argument, or returned from a function. Furthermore, first-class classes support dynamic inheritance: i.e., they can inherit from other classes at run time, enabling programmers to abstract over the inheritance hierarchy. Those features make first-class classes very
powerful and expressive, and enable highly modular and reusable pieces of code, such as:
const mixin = Base ⇒ {return class extends Base { ... }};In this piece of JavaScript code, mixin is parameterized by a class Base. Note that the concrete
implementation of Base can be even dynamically determined at run time, for example after
reading a configuration file to decide which class to use as the base class. When applied toan argument, mixin will create a new class on-the-fly and return that as a result. Later thatclass can be instantiated and used to create new objects, as any other classes.
In contrast, most statically typed languages do not have first-class classes and dynamic
inheritance. While all statically typed object-oriented languages allow first-class objects (i.e.,
objects can be passed as arguments and returned as results), the same is not true for classes.

95

7 First-Class Traits
Classes in languages such as Scala, Java or C++ are typically a second-class construct, andthe inheritance hierarchy is statically determined. The closest thing to first-class classes inlanguages like Java or Scala are classes such as java.lang.Class that enable representingclasses and interfaces as part of their reflective framework. java.lang.Class can be used
to mimic some of the uses of first-class classes, but in an essentially dynamically typed way.
Furthermore, simulating first-class classes using such mechanisms is highly cumbersome
because classes need to be manipulated programmatically. For example instantiating a newclass cannot be done using the standard new construct, but rather requires going throughAPI methods of java.lang.Class, such as newInstance, for creating a new instance of aclass.
Despite the popularity and expressive power of first-class classes in dynamically typed
languages, there is surprisingly little work on typing of first-class classes (or related concepts
such as first-class mixins or traits). First-class classes and dynamic inheritance pose wellknown difficulties in terms of typing. For example, in his thesis, Bracha [1992] comments
several times on the difficulties of typing dynamic inheritance and first-class mixins, and proposes the restriction to static inheritance that is also common in statically typed languages.
He also observes that such restriction poses severe limitations in terms of expressiveness, butthat appeared (at the time) to be a necessary compromise when typing was also desired. Only
recently some progress has been made in statically typing first-class classes and dynamic inheritance. In particular there are two works in this area: Racket’s gradually typed first-classclasses [Takikawa et al. 2012]; and Lee et al.’s model of typed first-class classes [Lee et al.
2015]. Both works provide typed models of first-class classes, and they enable encodings of
mixins [Bracha and Cook 1990] similar to those employed in dynamically typed languages.
However, as far as we known no previous work supports statically typed first-class traits.
Traits [Ducasse et al. 2006; Schärli et al. 2003] are an alternative to mixins, and other modelsof (multiple) inheritance. The key difference between traits and mixins lies on the treatment of conflicts when composing multiple traits/mixins. Mixins adopt an implicit resolution strategy for conflicts, where the compiler automatically picks one implementationin case of conflicts. For example, Scala uses the order of mixin composition to determine
which implementation to pick in case of conflicts. Traits, on the other hand, employ an explicit resolution strategy, where the compositions with conflicts are rejected, and the conflicts
are explicitly resolved by programmers. This gives programmers fine-grained control, whenconflicts arise, of selecting desired features from different components. Thus we believe traits
are a better model for multiple inheritance in statically typed object-oriented languages.

967.2 Overview

. Overview
This section aims at introducing first-class classes and traits, their possible uses and applications, as well as the typing challenges that arise from their use. We start by describing ahypothetical JavaScript library for text editing widgets, inspired and adapted from Racket’s
GUI toolkit [Takikawa et al. 2012]. The example is illustrative of typical uses of dynamic
inheritance/composition, and also the typing challenges in the presence of first-class classes/traits. Without diving into technical details, we then give the corresponding typed version
in SEDEL, and informally presents its salient features.

.. First-Class Classes in JavaScript
A class construct was officially added to JavaScript in the ECMAScript 2015 Language Specification [ECMA International 2015]. One purpose of adding classes to JavaScript was tosupport a construct that is more familiar to programmers who come from mainstream classbased languages, such as Java or C++. However classes in JavaScript are first-class and support
functionality not easily mimicked in statically typed class-based languages.
Conventional classes. Before diving into the more advanced features of JavaScript classes,
we first review the more conventional class declarations supported in JavaScript as well as
many other languages. Even for conventional classes there are some interesting points tonote about JavaScript that will be important when we move into a typed setting. An example
of a JavaScript class declaration is:class Editor {onKey(key) {return "Pressing " + key;}
doCut() {return this.onKey("C-x") + " for cutting text";}
showHelp() {return "Version: " + this.version() + " Basic usage...";}};

This form of class definition is standard and very similar to declarations in class-based languages (for example Java). The Editor class defines three methods: onKey for handling key
events, doCut for cutting text and showHelp for displaying help message. For the purpose ofdemonstration, we elide the actual implementation, and replace it with plain messages.

97

7 First-Class Traits
We wish to bring the reader’s attention to two points in the above class. Firstly, note thatthe doCut method is defined in terms of the onKey method via the keyword this . In otherwords the call to onKey is enabled by the self reference and is dynamically dispatched (i.e.,
the particular implementation of onKey will only be determined when the class or subclassis instantiated). Secondly, notice that there is no definition of the version method in theclass body, but such method is used in the body of the showHelp method. In a dynamically
typed language, such as JavaScript, using undefined methods is error prone—accidentally
instantiating Editor and then calling showHelp will cause a run-time error! Statically-typed
languages usually provide some means to protect us from this situation. For example, in
Java, we would need an abstract version method, which effectively makes Editor an abstractclass and prevents it from being instantiated. As we will see, SEDEL’s treatment of abstract
methods is quite different from mainstream languages. In fact, SEDEL has a unified (typing)mechanism for dealing with both dynamic dispatch and abstract methods. We will describe
SEDEL’s mechanism for dealing with both features and justify our design in Section 7.3.
First-class classes and class expressions. Another way to define a class in JavaScript
is via a class expression. This is where the class model in JavaScript is very different from thetraditional class model found in many mainstream object-oriented languages, such as Java,
where classes are second-class (static) entities. JavaScript embraces a dynamic class modelthat treats classes as first-class expressions: a function can take classes as arguments, or returnthem as a result. First-class classes enable programmers to abstract over patterns in the classhierarchy and to experiment with new forms of OOP such as mixins and traits. In particular,
mixins become programmer-defined constructs. We illustrate this by presenting a simple
mixin that adds spell checking to an editor:
const spellMixin = Base ⇒ {return class extends Base {check() {return super.onKey("C-c") + " for spell checking";}
onKey(key) {return "Process " + key + " on spell editor";}}};

Dynamic inheritance. In JavaScript, a mixin is simply a function with a superclass as
input and a subclass extending that superclass as an output. Concretely, spellMixin adds a98

7.2 Overview
method check for spell checking. It also provides a method onKey. The function spellMixin
shows the typical use of what we call dynamic inheritance. Note that Base, which is supposed
to be a superclass being inherited, is parameterized. Therefore spellMixin can be applied
to any base class at run time. This is impossible to do, in a type-safe way, in conventional
statically typed class-based languages like Java or C++.1
It is noteworthy that not all applications of spellMixin to base classes are successful. Notice the use of the super keyword in the check method. If the base class does not implement
the onKey method, then mixin application will fail with a run-time error. In a typed setting,a type system must express this requirement (i.e., the presence of the onKey method) on the(statically unknown) base class being inherited.
We invite the reader to pause for a while and think about what the type of spellMixin
would look like. Clearly our type system should be flexible enough to express this kind of
dynamic pattern of composition in order to accommodate mixins (or traits), but also not toolenient to allow any composition.

Mixin composition and conflicts. The powerful part of mixins is that spellMixin’sfunctionality is not tied to a particular class hierarchy and is composable with other features.
For example, we can define another mixin that adds simple modal editing—as in Vim—to
an arbitrary editor:
const modalMixin = Base ⇒ {return class extends Base {constructor() {super();
this.mode = "command";}toggleMode() {return "toggle succeeded";}
onKey(key) {return "Process " + key + " on modal editor";
}
};
};
1

With C++ templates, it is possible to implement a so-called mixin pattern [Smaragdakis and Batory 2000],
which enables extending a parameterized class. However C++ templates defer type-checking until instantiation, and such pattern still does not allow selection of the base class at run time (only at up to class instantiation time).

99

7 First-Class Traits
modalMixin adds a mode field that controls which keybindings are active, initially set to thecommand mode, and a method toggleMode that is used to switch between modes. It also
provides a method onKey.
Now we can compose spellMixin with modalMixin to produce a combination of functionality, mimicking some form of multiple inheritance:class IDEEditor extends modalMixin(spellMixin(Editor)) {version() {return 0.2;}}

The class IDEEditor extends the base class Editor with modal editing and spell checking
capabilities. It also defines the missing version method.
At first glance, IDEEditor looks quite fine, but it has a subtle issue. Recall that two mixins
modalMixin and spellMixin both provide a method onKey, and the Editor class also defines

an onKey method of its own. Now we have a name clash. A question arises as to which
one gets picked inside the IDEEditor class. A typical mixin model resolves this issue bylooking at the order of mixin applications. Mixins appearing later in the order overrides all
the identically named methods of earlier mixins. So in our case, onKey in modalMixin gets
picked. If we change the order of application to spellMixin(modalMixin(Editor)), thenonKey in spellMixin is inherited.

The problem of mixin composition. From the above discussion, we can see that mixin
are composed linearly: all the mixins used by a class must be applied one at a time. However,when we wish to resolve conflicts by selecting features from different mixins, we may not beable to find a suitable order. For example, when we compose the two mixins to make the class
IDEEditor, we can choose which of them comes first, but in either order, IDEEditor cannot

access to the onKey method from the Editor class.
Trait model. Because of the total ordering and the limited means for resolving conflicts
imposed by the mixin model, researchers have proposed a simple compositional model called
traits [Ducasse et al. 2006; Schärli et al. 2003]. Traits are lightweight entities and serve as theprimitive units of code reuse. Among others, the key difference from mixins is that the orderof trait composition is irrelevant, and conflicting methods must be resolved explicitly. This
gives programmers fine-grained control, when conflicts arise, of selecting desired featuresfrom different components. Thus we believe traits are a better model for multiple inheritancein statically typed object-oriented languages, and in SEDEL we realize this vision by giving

100

7.2 Overview
traits a first-class status in the language, achieving more expressive power compared withtraditional (second-class) traits.
Summary of typing challenges. From our previous discussion, we can identify the following typing challenges for a type system to accommodate the programming patterns (firstclass classes/mixins) we have just seen:• How to account for, in a typed way, abstract methods and dynamic dispatch.
• What are the types of first-class classes or mixins.
• How to type dynamic inheritance.
• How to express constraints on method presence and absence (the use of super clearly
demands that).
• In the presence of first-class traits, how to detect conflicts statically, even when thetraits involved are not statically known.
SEDEL elegantly solves the above challenges in a unified way, as we will see next.

.. A Glance at Typed First-Class Traits in SEDEL
We now rewrite the above code in SEDEL, but this time with types. The resulting code has
the same functionality as the dynamic version, but it is statically typed. All code snippets inthis and later sections are runnable in our prototype. Before proceeding, we ask the reader tobear in mind that in this section we are not using traits in the most canonical way, i.e., we use
traits as if they are classes (but with built-in conflict detection). This is because we are trying
to stay as close as possible to the structure of the JavaScript code for ease of comparison. In
Section 7.3 we will remedy this to make better use of traits.
Simple traits. Below is a simple trait editor, which corresponds to the JavaScript class
Editor. The editor trait defines the same set of methods: on_key, do_cut and show_help:
trait editor [self : Editor & Version] ⇒ {on_key(key : String) = "Pressing " ++ key;
do_cut = self.on_key "C-x" ++ " for cutting text";
show_help = "Version: " ++ self.version ++ " Basic usage..."};101

7 First-Class Traits
The first thing to notice is that SEDEL uses a syntax (similar to Scala’s self type annotations [Odersky et al. 2004]) where we can give a type annotation to the self reference. In
the type of self we use & construct to create intersection types. Editor and Version are tworecord types:
type Editor = {on_key : String → String,
do_cut : String,show_help : String};
type Version = {version : String};

For the sake of conciseness, SEDEL uses type aliases to abbreviate types.
The type of self encodes abstract methods. Recall that in the JavaScript class Editor,
the version method is undefined, but is used inside showHelp. How can we express this in
the typed setting, if not with an abstract method? In SEDEL, the type of self plays the roleof trait requirements. As a first approximation, the invocation of version on self is justified
by noticing that (part of) the type of self (i.e., Version) contains the declaration of version.
An interesting aspect of SEDEL’s trait model is that there is no need for abstract methods.
Instead, abstract methods can be simulated as requirements of a trait. Later, when the trait
is composed with other traits, all requirements on the type of self must be satisfied and oneof the traits in the composition must provide an implementation of the method version.
As with the JavaScript code, the on_key method is invoked on self in the body of do_cut.
This is allowed as (part of) the type of self (i.e., Editor) contains the signature of on_key.
Compared to the JavaScript class Editor, almost everything stays the same, except that we
now have a typed version. As a side note, since SEDEL is currently a pure “functional” objectoriented language, there is no difference between fields and methods, so we can omit empty
arguments and parameter parentheses.
First-class traits and trait expressions. SEDEL treats traits as first-class expressions,putting them in the same syntactic category as objects, functions, and other primitive forms.
To illustrate this, we give the SEDEL version of spellMixin:
type Spelling = {check : String};
type OnKey = {102

7.2 Overview
on_key : String → String};
spell_mixin [A * Spelling & OnKey](base : Trait[Editor & Version, Editor & A]) =trait [self : Editor & Version] inherits base ⇒ {override on_key(key : String) = "Process" ++ key ++ "on spell editor";check = super.on_key "C-c" ++ " for spelling check"};

This looks daunting at first, but spell_mixin has almost the same structure as its JavaScript
counterpart spellMixin, albeit with some type annotations. In SEDEL, we use capital letters(A, B, . . . ) to denote type variables, and trait expressions trait [self : ...] inherits ...
⇒ {...} to create first-class traits. Trait expressions have trait types of the form Trait[T1,T2] where T1 and T2 denote trait requirements and functionality respectively. We will explain

trait types in Section 7.3. Despite the structural similarities, there are several significant features that are unique to SEDEL (e.g., the disjointness operator *). We discuss these in thefollowing.
Disjoint polymorphism and conflict detection. SEDEL uses a type system based on
disjoint intersection types (cf. Chapter 3) and disjoint polymorphism (cf. Chapter 4). Disjoint
intersections empower SEDEL to detect conflicts statically when trying to compose two traits
with identically named features. For example, composing two traits a and b that both provide
foo gives a type error (the overloaded & operator denotes trait composition):trait a ⇒ { foo = 1 };trait b ⇒ { foo = 2 };trait c inherits a & b ⇒ {}; -- type error!

Disjoint polymorphism, as a more advanced mechanism, allows detecting conflicts even in
the presence of polymorphism—for example when a trait is parameterized and its full setof methods is not statically known. As can be seen, spell_mixin is actually a polymorphic
function. Unlike ordinary parametric polymorphism, in SEDEL, a type variable can also
have a disjointness constraint. For instance, A * Spelling & OnKey means that A can beinstantiated to any type as long as it does not contain check and on_key. Note that these are
the minimal constraints of A, as (1) A cannot contain the on_key method because otherwise
it will conflict with Editor; (2) A cannot contain the check method because otherwise it will
conflict with that in the trait body. To mimic mixins, the argument base, which is supposed
to be some trait, serves as the “base” trait being inherited. Notice that the type variable A
appears in the type of base, which essentially states that base is a trait that contains at least103

7 First-Class Traits
those methods specified by Editor, and possibly more (which we do not know statically).
Also note that leaving out the override keyword will result in a type error. The type system
is forcing us to be very specific as to what is the intention of the on_key method because
it sees the same method is also declared in base, and blindly inheriting base will definitely
cause a method conflict. As a final note, the use of super inside check is allowed because the“super-trait” base implements on_key, as can be seen from its type.
Typing dynamic inheritance. Disjoint polymorphism enables us to correctly type dynamic inheritance: spell_mixin is able to take any trait that conforms with its assigned
type, equips it with the check method and overrides its old on_key method. As a side note,the use of disjoint polymorphism is essential to correctly model the mixin semantics. From
the type we know base has some features specified by Editor, plus something more denotedby A. By inheriting base, it is guaranteed that the resulting trait will have everything thatis already contained in base, plus more features. This is in some sense similar to row polymorphism [Wand 1994] in that the result trait is prohibited from forgetting methods fromthe argument trait. (Section 10.3 has further discussion about the relationship between row
polymorphism and disjoint polymorphism.)
Typing mixin composition. Next we give the typed version of modalMixin as follows:
type ModalEdit = {mode : String, toggle_mode : String};
modal_mixin [A * ModalEdit & OnKey](base : Trait[Editor & Version, Editor & A]) =trait [self : Editor & Version] inherits base ⇒ {override on_key(key : String) = "Process" ++ key ++ "on modal editor";mode = "command";toggle_mode = "toggle succeeded"};

Now the definition of modal_mixin should be self-explanatory. Finally we can apply both“mixins” to editor one at a time to create an IDE editor:
type IDEEditor = Editor & Version & Spelling & ModalEdit;
trait ide_editor [self : IDEEditor]
inherits modal_mixin Spelling (spell_mixin ⊤ editor) ⇒ {version = "0.2"};

1047.3 Typed First-Class Traits
As with the JavaScript class IDEEditor, we need to fill in the missing version method. It
is easy to verify that the on_key method in modal_mixin is inherited. Compared with theuntyped version, here this behavior is reasonable because we specifically tag each on_key
method to be an overriding method. Let us take a close look at the mixin applications.
Since SEDEL is currently explicitly typed, we need to provide concrete types when applying modal_mixin and spell_mixin. In the inner application (spell_mixin ⊤ editor), we
use the top type ⊤ to instantiate A because the editor trait provides exactly those method
specified by Editor and nothing more (hence ⊤). In the outer application, we use Spellingto instantiate A because the resulting trait of the inner application contains the check method.
In summary, mixin applications are simply normal function applications, and conflict resolution code is implicitly embedded via the keyword override and the order of mixin applications. Unsurprisingly, changing the mixin application order toinherits spell_mixin ModalEdit (modal_mixin ⊤ editor)

gives the same difference as in the JavaScript version (on_key from spell_mixin is inherited).
Admittedly the typed version is unnecessarily complicated as we were mimicking mixins
by functions over traits. The final trait ide_editor suffers from the same problem as the class
IDEEditor, since there is no obvious way to access the on_key method in the editor trait.2

Section 7.3 makes better use of traits to simplify the editor code.

. Typed First-Class Traits
In Section 7.2 we have seen some examples of first-class traits at work in SEDEL. In this section we give a detailed account of SEDEL’s support for typed first-class traits, to complement
what has been presented so far. In doing so, we simplify the examples in Section 7.2 to makebetter use of traits. Section 7.4 presents the formal type system of first-class traits.

.. Traits in SEDELSEDEL supports a simple, yet expressive form of traits [Schärli et al. 2003]. Traits provide a
simple mechanism for fine-grained code reuse, which can be regarded as a disciplined formof multiple inheritance. A trait is similar to a mixin in that it encapsulates a collection ofrelated methods to be added to a class. The practical difference between traits and mixins is
the way conflicting features that typically arise in multiple inheritance are dealt with. Insteadof automatically resolved by scoping rules, conflicts are, in SEDEL, detected by the typesystem, and explicitly resolved by the programmer. Compared with traditional trait models,2

In fact, as we will see in Section 7.3.5, we can still access it by the forwarding operator.

105

7 First-Class Traits
there are three interesting points about SEDEL’s traits: (1) they are statically typed; (2) they
are first-class values; (3) they support dynamic inheritance. The support for such combinationof features is one of the key novelties of SEDEL. Another minor difference from traditional
traits (e.g., in Scala) is that, due to the use of structural types, a trait name is not a type.

.. Two Roles of Traits in SEDEL
Traits as templates for creating objects. An obvious difference between traits in
SEDEL and many other models of traits [Fisher and Reppy 2004; Odersky and Zenger 2005;
Schärli et al. 2003] is that they directly serve as templates for objects. In many other trait
models, traits are complemented by classes, which take the responsibility for object creation.
In particular, most models of traits do not allow constructors for traits. However, a trait
in SEDEL has a single constructor of the same name. Take our last trait ide_editor in
Section 7.2 for example:
a_editor1 = new[IDEEditor] ide_editor;

As with conventional object-oriented languages, the keyword new is used to create an object. A difference to other object-oriented languages is that the keyword new also specifies
the intended type of the object. We instantiate the ide_editor trait and create an object
a_editor1 of type IDEEditor. As we will see in Section 7.3.4, constructors with parameters

can also be expressed.
It is tempting to instantiate the editor trait such as new[Editor] editor. However thiswould result in a type error, because, as we discussed, editor has no definition of version,and blindly instantiating it would cause run-time errors. This behavior is on a par with Java’s
abstract classes—i.e., traits with undefined methods cannot be instantiated on their own.
Traits as units of code reuse. The traditional role of traits is to serve as units of code
reuse. SEDEL’s traits can have this role as well. Our spell_mixin function in Section 7.2 ismore complicated than it should be. This is because we were mimicking classes as traits, andmixins as functions over traits. Instead, traits already provide a mechanism of code reuse.
To illustrate this, we simplify spell_mixin as follows:
trait spell [self : OnKey] ⇒ {on_key(key : String) = "Process " ++ key ++ " on spell editor";check = self.on_key "C-c" ++ " for spell checking"};This is much cleaner. The trait spell adds a method check. It also defines a method on_key.
A key difference from spell_mixin is that on_key is invoked on the self parameter instead106

7.3 Typed First-Class Traitsof super. Note that this does not necessarily mean check will call on_key defined in the same
trait. As we will see, the actual behavior entirely depends on how we compose spell withother traits. One minor difference is that we do not need to tag on_key with the override
keyword, because spell stands as a standalone entity. Another interesting point is that thetype of self (i.e., OnKey) is not the same as that of the trait body, which also contains thecheck method. In SEDEL’s traits, the type of self serves as trait requirements.

Classes and/or traits. In the literature on traits [Ducasse et al. 2006; Schärli et al. 2003],
the aforementioned two roles are considered as competing. One reason of the two roles
conflicting in class-based languages is because a class must adopt a fixed position in the classhierarchy and therefore it can be difficult to reuse and resolve conflicts, whereas in SEDEL,a trait is a standalone entity and is not tied to any particular hierarchy. Therefore we canview our traits either as templates for creating objects, or as units of code reuse. Anotherimportant reason why our model can do just with traits is because we have a pure language.
Mutable state can often only appear in classes in imperative models of traits, which is a goodreason for having both classes and traits.

.. Trait Types and Trait Requirements
Object types and trait types. SEDEL adopts a relatively standard foundational modelof object-oriented constructs [Lee et al. 2015] where objects are encoded as records with astructural type. This is why the object a_editor1 has the record type IDEEditor. In SEDEL,
an object type is different from a trait type. A trait type is specified via Trait[T1, T2].
Trait requirements and functionality. In general, a trait type Trait[T1, T2] specifies both the requirements T1 and the functionality T2 of a trait. The requirements of a trait
denote the types/methods that the trait needs to support for defining the functionality it provides. For example, spell has type Trait[OnKey, OnKey & Spelling], meaning that spell
requires some implementation of the on_key method, and it provides implementations forthe on_key and check methods. When a trait has no requirements, the absence of a requirement is denoted by using the top type ⊤. A simplified sugar Trait[T] is used to denote a trait
without requirements, but providing functionality T.
Trait requirements as abstract methods. Let us go back to our very first trait editor
in Section 7.2. Note how in editor the type of the self parameter is Editor & Version,
where Version contains a declaration of the version method that is needed for the definition of show_help. Note also that the trait itself does not actually contain a version defini-107

7 First-Class Traits
tion. In many other object-oriented models a similar program could be achieved by having
an abstract definition of version. In SEDEL there are no abstract definitions (methods orfields), but a similar result can be achieved via trait requirements. Requirements of a trait
are met at the object creation point. For example, as we mentioned before, the editor trait
alone cannot be instantiated since it lacks version. However, when it is composed with atrait that provides version, the composition can be instantiated, as shown below:
trait foo ⇒ {version = "0.2"};bar = new[Editor & Version] foo & editor;

SEDEL uses a syntax where the self parameter can be explicitly named (not necessarily
named self ) with a type annotation. When the self parameter is omitted (for example in
the foo trait above), its type defaults to ⊤. This is different from most object-oriented languages, where the default type of the self parameter is the same as the class being defined.
This also makes trait requirements “pay as you go” in the sense that if the self parameter is
not used in the body, then there is no requirements on the trait. Otherwise, suppose the typeof the self parameter in the trait foo implicitly defaults to Version:
trait foo [self : Version] ⇒ {version = "0.2"};

then Version will pollute the type of the self parameter of any trait that uses foo, cascading
down the inheritance hierarchy, even though self is not used in the body of foo.
Intersection types model subtyping. The type IDEEditor is defined as an intersection
type. Intersection types [Coppo et al. 1981; Pottinger 1980] have been woven into many
modern languages these days. A notable example is Scala, which makes fundamental use ofintersection types to express a class/trait that extends multiple other traits. An intersection
type such as T1 & T2 contains exactly those values which can be used as values of type T1 andof type T2, and as such, T1 & T2 immediately introduces a subtyping relation between itself
and its two constituent types T1 and T2. Unsurprisingly, IDEEditor is a subtype of Editor.

.. Traits with Parameters and First-Class Traits
So far our uses of traits involve no parameters. Instead of inventing another trait syntax withparameters, a trait with parameters is just a function that produces a trait expression, sincefunctions already have parameters of their own. This is one benefit of having first-class traits108

7.3 Typed First-Class Traits
in terms of language economy. To illustrate, let us simplify modal_mixin in a similar way asin spell_mixin:
modal (init_mode : String) = trait ⇒ {on_key(key : String) = "Process " ++ key ++ " on modal editor";mode = init_mode;
toggle_mode = "toggle succeeded"};

The first thing to notice is that modal is a function with one argument, and returns a trait expression, which essentially makes modal a trait with one parameter. Now it is easy to see that a
trait declaration trait name [self : ...] ⇒ {...} is just syntactic sugar for function definition name = trait [self : ...] ⇒ {...}. The body of the modal trait is straightforward.
We initialize the mode field to init_mode. The modal trait also comes with a constructor with
one parameter—e.g., we can create an object via new[ModalEdit] (modal "insert").

.. Detecting and Resolving Conflicts in Trait Composition
A common problem in multiple inheritance is how to detect and/or resolve conflicts. For
example, when inheriting from two traits that have the same field, then it is unclear which
implementation to choose. There are various approaches to dealing with conflicts. The traitbased approach requires conflicts to be resolved at the level of the composition, otherwise theprogram is rejected by the type system. SEDEL provides a means to help resolve conflicts.
We start by assembling all the traits defined in this section to create the final editor withthe same functionality as ide_editor in Section 7.2. Our first try is as follows:
ide_editor (init_mode : String) =-- conflicttrait [self : IDEEditor] inherits editor & spell & modal init_mode ⇒ {version = "0.2"};

Unfortunately the above trait gets rejected by SEDEL because editor, spell and modal all
define an on_key method. Recall that in Section 7.2, when we use a mixin-style composition,
the conflict resolution code has been hardwired in the definition. However, in a trait-stylecomposition, this is not the case: conflicts must be resolved explicitly. The above definition is
ill-typed precisely because there is a conflicting method on_key, thus violating the disjointness conditions imposed by disjoint intersection types.
Resolving conflicts. To resolve the conflict, we need to explicitly state which implementation of the method on_key gets to stay. SEDEL provides such a means—the exclusion

109

7 First-Class Traits
operator (denoted by \)—which allows one to exclude a field/method from a given trait. The
following matches the behavior in Section 7.2 where on_key from the modal trait is selected:
ide_editor (init_mode : String) =trait [self : IDEEditor]
inherits (editor \ {on_key : String → String})& (spell \ {on_key : String → String})& (modal init_mode) ⇒ {version = "0.2"};

Now the above code type checks. We can also select on_key from the spell trait as easily:
ide_editor2 (init_mode : String) =trait [self : IDEEditor]
inherits spell& (editor \ {on_key : String → String})& (modal init_mode) \ {on_key : String → String} ⇒ {version = "0.2"};

In Section 7.2 we mentioned that in the mixin style, it is impossible to select on_key fromthe editor trait, but this is not a problem now:
ide_editor3 (init_mode : String) =trait [self : IDEEditor]
inherits editor& (spell \ {on_key : String → String})& ((modal init_mode) \ {on_key : String → String}) ⇒ {version = "0.2"};

Using the exclusion operator, we can drop on_key in spell and modal while keeping it ineditor.

The forwarding operator. Another operator that SEDEL provides is the forwarding
operator, which can be useful when we want to access some method that has been explicitly
excluded in the inherits clause. This is a common scenario in diamond inheritance, where
super is not enough. Below we show a variant of ide_editor:
ide_editor4 (init_mode : String) =trait [self : IDEEditor]
inherits (editor \ {on_key : String → String})& (spell \ {on_key : String → String})

1107.4 Formalizing Typed First-Class Traits& (modal init_mode) ⇒ {version = "0.2";
override on_key(key : String) =super.on_key key ++ " and " ++ (spell ^ self).on_key key};

Notice that on_key in spell has been excluded. However, we can still access it by using
the forwarding operator as in spell ^ self, which gives full access to all the methods inspell. Also note that using super only gives us access to on_key in the modal trait. To see
ide_editor4 in action, we create a small test:
a_editor2 = new[IDEEditor] (ide_editor4 "command");main = a_editor2.do_cut-- Output:-- "Process C-x on modal editor and Process C-x on spell editor for
cutting text"

.. Disjoint Polymorphism and Dynamic Composition
SEDEL supports disjoint polymorphism. The combination of disjoint polymorphism andfirst-class traits enables the highly modular code where traits with statically unknown types
can be instantiated and composed in a type-safe way! The following is illustrative of this:mergeTraits A [B * A] (x : Trait[A]) (y : Trait[B]) = new[A & B] x & y;

The mergeTraits function takes two traits x and y of some arbitrary types Trait[A] andTrait[B], composes them, and creates an object from the resulting composed trait. Clearlysuch composition cannot always work if A and B can have conflicts. However, mergeTraits
has a constraint B * A that ensures that whatever types are used to instantiate A and B theymust be disjoint. Thus, under the assumption that A and B are disjoint the code type-checks.

. Formalizing Typed First-Class Traits
This section presents the syntax and semantics of SEDEL. In particular, we show how toelaborate high-level source language constructs (self-references, abstract methods, first-classtraits, dynamic inheritance and so on) to F+
i . The treatment of the self-reference and dynamic
dispatching is inspired by Cook’s work on the denotational semantics for inheritance [Cook1989; Cook and Palsberg 1989]. We then prove the elaboration is type safe, i.e., well-typed
SEDEL expressions are translated to well-typed F+i terms. Finally we show that SEDEL iscoherent. All manual proofs about SEDEL can be found in Appendix C.

111

7 First-Class Traits
Types
Expressions

Value Contexts
Type Contexts

A, B, C
T

Γ
∆

::=
::=

⊤ | Int | A → B | A & B | {l : A} | α | ∀(α ∗ A). B | Trait [A, B]⊤ | i | x | λx.T | T1 T2 | Λ(α ∗ A). T | T A | T1 , , T2 | T : A|{l = T } | T .l | letrec x : A = T1 in T2 | new [A]( Ti ) | T1 ^ T2|

trait [self : B] inherits Ti { lj = Tj ′ } : A

::=
::=

• | Γ, x : A• | ∆, α ∗ A

i

i

j

Figure 7.1: SEDEL core syntax and syntactic abbreviations

.. Syntax
The core syntax of SEDEL is shown in Fig. 7.1, with trait related constructs highlighted .
For brevity of the meta-theoretic study, we do not consider definitions, which can be addedin standard ways.
Types. Metavariables A, B, C range over types. Types include a top type ⊤, integers Int,
function types A → B, intersection types A & B, singleton record types {l : A}, type variables α and disjoint (universal) quantification ∀(α ∗ A). B. The main novelty is the type offirst-class traits Trait [A, B], which expresses the requirement A and the functionality B. We
will use [A/α]B to denote capture-avoiding substitution of A for α inside B.
Expressions. Metavariable T ranges over expressions. We start with constructs required
to encode objects based on records: term variables x, lambda abstractions λx.T , function
applications T1 T2 , singleton records {l = T }, record projections T .l, recursive let bindings
letrec x : A = T1 in T2 , disjoint type abstraction Λ(α ∗ A). T and type application T A. The
calculus also supports a merge construct T1 , , T2 for creating values of intersection types andannotated expressions T : A. We also include a canonical top value ⊤ and literals i.
First-class traits and trait expressions. Using the vector notation T to indicate asequence of zero or more T (i.e., T1 , . . . , Tn ), the central construct of SEDEL is the trait exi

jpression trait [self : B] inherits Ti { lj = Tj ′ } : A, which specifies a list of trait expressions
Ti in the inherits clause, an explicit self parameter (with type annotation B), and a set of
methods {lj = Ej′ }. Intuitively this trait expression has type Trait [B, A]. Unlike the conventional trait model, a trait expression denotes a first-class value: it may occur anywhere
i

where an expression is expected. Trait instantiation expressions new [A]( Ti ) instantiate
a composition of trait expressions Ti to create an object of type A. Finally T1 ^ T2 is theforwarding expression, where T1 should be some trait.

112

7.4 Formalizing Typed First-Class Traits∆⊢A(Well-formedness of types)

WF-topWF-int∆⊢⊤∆ ⊢ Int

WF-rcd∆⊢A∆ ⊢ {l : A}

WF-arr
∆⊢A
∆⊢B
∆⊢A→B

WF-forall∆⊢A∆, α ∗ A ⊢ B∆ ⊢ ∀(α ∗ A). B

WF-and∆⊢A∆⊢B∆ ⊢ A&B

WF-var(α ∗ A) ∈ ∆∆⊢α

WF-trait∆⊢A∆⊢B∆ ⊢ Trait [A, B]

A <: B(Subtyping)

TS-reflA <: A

TS-trans
A2 <: A3A1 <: A2A1 <: A3

TS-arr
B1 <: A1A2 <: B2
A1 → A2 <: B1 → B2
TS-andA1 <: A2A1 <: A3A1 <: A2 & A3

TS-topA <: ⊤

TS-rcdA <: B{l : A} <: {l : B}

TS-andL

TS-andR

A1 & A2 <: A1

A1 & A2 <: A2

TS-distArr

TS-topArr(A1 → A2 ) & (A1 → A3 ) <: A1 → A2 & A3

⊤ <: ⊤ → ⊤TS-distRcd

TS-topRcd{l : A} & {l : B} <: {l : A & B}

⊤ <: {l : ⊤}TS-trait
B1 <: A1A2 <: B2
Trait [A1 , A2 ] <: Trait [B1 , B2 ]Figure 7.2: Well-formedness and subtyping of SEDEL

.. Semantics
Subtyping and well-formedness. Figure 7.2 shows the well-formedness and subtyping
rules for SEDEL. The well-formedness rule for trait types (WF-trait) is straightforward.
The subtyping rule for trait types (TS-trait) resembles the one for function types in that it
is contravariant on the first type A and covariant on the second type B. The rest of the rules
are direct analogies of F+i .
Disjointness. Figure 7.3 shows the disjointness rules for traits. The disjointness checking
is the underlying mechanism of conflict detection. We naturally extend the disjointness rules
in F+
i to cover trait types. Here we discuss the rules related with traits. Rule SD-trait saysthat as long as the functionalities that two traits provide are disjoint, the two trait types are
disjoint. Rules SD-traitArr and SD-traitArr deal with situations where one of the two

113

7 First-Class Traits∆⊢A∗B(Disjointness)

SD-topL

SD-topR

∆⊢⊤∗A

∆⊢A∗⊤

SD-arr∆ ⊢ A 2 ∗ B2
∆ ⊢ A 1 → A 2 ∗ B1 → B 2

SD-andL∆ ⊢ A1 ∗ B∆ ⊢ A2 ∗ B∆ ⊢ A1 & A2 ∗ B

SD-rcdEq∆⊢A∗B∆ ⊢ {l : A} ∗ {l : B}

SD-andR∆ ⊢ A ∗ B1
∆ ⊢ A ∗ B2
∆ ⊢ A ∗ B1 & B2

SD-tvarL(α ∗ A) ∈ ∆A <: B∆⊢α∗B

SD-tvarR(α ∗ A) ∈ ∆A <: B∆⊢B∗αSD-forall∆, α ∗ A1 & A2 ⊢ B1 ∗ B2
∆ ⊢ ∀(α ∗ A1 ). B1 ∗ ∀(α ∗ A2 ). B2
SD-traitArr
∆ ⊢ A 2 ∗ B2
∆ ⊢ Trait [A1 , A2 ] ∗ B1 → B2

SD-rcdNeq
l1 ̸= l2
∆ ⊢ {l1 : A} ∗ {l2 : B}

SD-trait∆ ⊢ A 2 ∗ B2
∆ ⊢ Trait [A1 , A2 ] ∗ Trait [B1 , B2 ]SD-traitArr
∆ ⊢ A 2 ∗ B2
∆ ⊢ A1 → A2 ∗ Trait [B1 , B2 ]A ∗ax B
SDax-sym
B ∗ax AA ∗ax BSD-ax
A ∗ax B∆⊢A∗B

(Disjointness axiom)
SDax-intArr

SDax-intRcd

SDax-intAll

Int ∗ax A1 → A2

Int ∗ax {l : A}

Int ∗ax ∀(α ∗ B1 ). B2

SDax-arrAll

SDax-arrRcd

SDax-allRcd

A1 → A2 ∗ax ∀(α ∗ B1 ). B2

A1 → A2 ∗ax {l : B}∀(α ∗ A1 ). A2 ∗ax {l : B}

SDax-intTrait

SDax-traitAll

SDax-traitRcd

Int ∗ax Trait [A1 , A2 ]Trait [A1 , A2 ] ∗ax ∀(α ∗ B1 ). B2

Trait [A1 , A2 ] ∗ax {l : B}

Figure 7.3: Disjointness rules of SEDEL

114

7.4 Formalizing Typed First-Class Traits
types is a function type. At first glance, these two look strange because a trait type is different
from a function type, and they ought to be disjoint as an axiom. The reason is that SEDEL
has an elaboration semantics, and as we will see, trait types are translated to function types.
In order to ensure the type safety of elaboration, we have to have special treatment for trait
and function types. In principle, if SEDEL has its own dynamic semantics, then trait types
are always disjoint with function types.

Typing traits. The typing rules of trait related constructs are shown in Fig. 7.4. The reader
is advised to ignore the translation parts (⇝ E) for now. As with F+
i , SEDEL employs
two modes: the inference mode (⇒) and the checking mode (⇐). The inference judgment∆; Γ ⊢ T ⇒ A says that we can synthesize a type A for expression T . The checking
judgment ∆; Γ ⊢ T ⇐ A checks T against A.
To type-check a trait (rule ST-trait) we first type-check if its inherited traits Ti are valid
traits. Note that each trait Ti can possibly refer to self. Methods must all be well-typed in
the usual sense. Apart from these, we have several side-conditions to make sure traits are
well-behaved. The disjointness judgment ∆ ⊢ C1 ∗ .. ∗ Cn ∗ C ensures that we do not haveconflicting methods (in inherited traits and the body). The subtyping judgments B <: Bi
ensure that the self parameter satisfies the requirements imposed by each inherited trait.
Finally the subtyping judgment C1 & .. & Cn & C <: A sanity-checks that the assigned type
A is compatible.
Trait instantiation (rule ST-new) requires that each instantiated trait is valid. There arealso several side-conditions, which serve the same purposes as in rule ST-trait. Rule STforward says that the first operand T1 of the forwarding operator must be a trait. Moreover,
the type of the second operand T2 must satisfy the requirement of T1 .

Treatments of super, exclusion and override. We also include a variant (rule STtraitSuper) of rule ST-trait where it implicitly assumes a super variable pointing to theinherits clause in the context when type checking the trait body.
ST-traitSuper∆; Γ, self : B ⊢ Ti ⇒ Trait [Bi , Ci ] ⇝ Ei∆; Γ, self : B, super : C1 & .. & Cn ⊢ { lj = Tj ′B <: Bii∈1..n∆ ⊢ C1 ∗ .. ∗ Cn ∗ C∆; Γ ⊢ trait [self : B] inherits Ti

i∈1..nj∈1..m} ⇒ C⇝E

C1 & .. & Cn & C <: A

{ lj = Tj ′λself : |B|. (let super = (Ei self)i∈1..nj∈1..mi∈1..n} : A ⇒ Trait [B, A] ⇝in super , , E)

115

7 First-Class Traits∆; Γ ⊢ T ⇒ A ⇝ E(Inference)

ST-topST-int∆; Γ ⊢ ⊤ ⇒ ⊤ ⇝ ⊤∆; Γ ⊢ i ⇒ Int ⇝ i

ST-var(x : A) ∈ ∆∆; Γ ⊢ x ⇒ A ⇝ x

ST-app∆; Γ ⊢ T1 ⇒ A1 → A2 ⇝ E1∆; Γ ⊢ T2 ⇐ A1 ⇝ E2∆; Γ ⊢ T1 T2 ⇒ A2 ⇝ E1 E2
ST-merge∆; Γ ⊢ T1 ⇒ A ⇝ E1∆; Γ ⊢ T2 ⇒ B ⇝ E2∆; Γ ⊢ T1 , , T2 ⇒ A & B ⇝ E1 , , E2
ST-anno∆; Γ ⊢ T ⇐ A ⇝ E∆; Γ ⊢ T : A ⇒ A ⇝ E : |A|∆⊢A∗B

ST-tabs∆⊢A∆, α ∗ A; Γ ⊢ T ⇒ B ⇝ E∆; Γ ⊢ Λ(α ∗ A). T ⇒ ∀(α ∗ A). B ⇝ Λ(α ∗ |A|). E

ST-tapp∆; Γ ⊢ T ⇒ ∀(α ∗ B1 ). B2 ⇝ E∆⊢A∆ ⊢ A ∗ B1∆; Γ ⊢ T A ⇒ [A/α]B2 ⇝ E |A|
ST-proj∆; Γ ⊢ T ⇒ {l : A} ⇝ E∆; Γ ⊢ T .l ⇒ A ⇝ E.l

ST-rcd∆; Γ ⊢ T ⇒ A ⇝ E∆; Γ ⊢ {l = T } ⇒ {l : A} ⇝ {l = E}
ST-trait∆; Γ, self : B ⊢ Ti ⇒ Trait [Bi , Ci ] ⇝ Ei∆; Γ, self : B ⊢ { lj = Tj ′B <: Bii∈1..nj∈1..m∆ ⊢ C1 ∗ .. ∗ Cn ∗ C∆; Γ ⊢ trait [self : B] inherits Ti

i∈1..n{ lj = Tj

λself : |B|. (((Ei self)
ST-newi∈1..n} ⇒ C⇝E

C1 & .. & Cn & C <: Aj∈1..m′i∈1..n} : A ⇒ Trait [B, A] ⇝) , , E)i∈1..n∆; Γ ⊢ Ti ⇒ Trait [Ai , Bi ] ⇝ Ei
i∈1..nA <: Ai∆ ⊢ B1 ∗ .. ∗ Bn
B1 & .. & Bn <: A∆; Γ ⊢ new [A]( Tii∈1..n) ⇒ A ⇝ letrec self : |A| = (Ei self)i∈1..nin self

ST-forward∆; Γ ⊢ T1 ⇒ Trait [A, B] ⇝ E1∆; Γ ⊢ T2 ⇐ A ⇝ E2∆; Γ ⊢ T1 ^ T2 ⇒ B ⇝ E1 E2∆; Γ ⊢ T ⇐ A ⇝ E(Checking)ST-abs∆⊢A∆; Γ, x : A ⊢ T ⇐ B ⇝ E∆; Γ ⊢ λx.T ⇐ A → B ⇝ λx. E

116

ST-sub∆; Γ ⊢ T ⇒ A ⇝ E
A <: B∆; Γ ⊢ T ⇐ B ⇝ E

Figure 7.4: Typing rules of SEDEL∆⊢B

7.4 Formalizing Typed First-Class Traits
One may have also noticed that in Fig. 7.1 we did not include the exclusion operator in thecore SEDEL syntax, neither does override appear. The reason is that in principle all uses ofthe exclusion operator can be replaced by type annotations. For example to exclude a bar
field from {foo = a, bar = b, baz = c}, all we need is to annotate the record with type{foo : A, baz : C} (suppose a has type A, etc). By the subsumption rule, the resulting

record is guaranteed to contain no bar field. In the same vein, the use of override can be
explained using the exclusion operator. We omit all of these features in the meta-theoretic
study in order to focus our attention on the essence of first-class traits. However in practice,
this is rather inconvenient as we need to write down all types we wish to retain rather thanthe one to exclude. So in our implementation we offer all of them.
Elaboration. The operational semantics of SEDEL is given by means of a type-directedtranslation into F+i extended with (lazy) recursive let bindings. This extension is standard
and type-safe. Let us go back to Fig. 7.4, now focusing on the translation parts, which are
regular F+i terms. Most of them are straightforward translations and are thus omitted. We
explain the most involved rules regarding traits. In rule Inf-trait, a trait is translated intoa lambda abstraction with self as the formal parameter. In essence a trait corresponds towhat Cook and Palsberg [1989] call a generator. The translations of the inherited traits(i.e., Ei ) are each applied to self and then merged with the translation of the trait body E.
Now it is clear why we require B (the type of self) to be a subtype of each Bi (the requirement of each inherited trait). Note that we abuse the vector notation here with the intention that (Ei self)i∈1..nmeans E1 self , , .. , , En self. Here is an example of translating theide_editor trait from Section 7.2 into plain F+
i terms equipped with definitions (suppose
modal_mixin and spell_mixin have been translated accordingly):
ide_editor (self : IDEEditor) =(modal_mixin Spelling (spell_mixin ⊤ editor) self) ,, {version = "0.2"};

The translation of the super keyword in rule ST-traitSuper is also straightforward. That is,
it becomes a let binding super with the value (Ei self)i∈1..n, enabling access to the inherited

traits.
Rule ST-new show the translation of trait instantiation. First we apply every translation(i.e., Ei ) of the instantiated traits to the self parameter, and then merge the applications together. The bar notation is interpreted similarly to the translation in rule ST-trait. Finally
we compute the lazy fixed-point of the resulting merge term, i.e., self-reference must be updated to refer to the whole composition. Taking the fixed-point of the traits/generators again
follows the denotational inheritance model by Cook and Palsberg [1989]. This is the key tothe correct implementation of dynamic dispatching. Finally, rule ST-forward translates117

7 First-Class Traits
forwarding expressions to function applications. We show the translation of the a_editor1
object in Section 7.3 to illustrate the translation of instantiation:a_editor1 = letrec self : IDEEditor = ide_editor self in self;

One remarkable point is that, while Cook’s work is done in an untyped setting, here weapply their ideas in a setting with disjoint intersection types and disjoint polymorphism. Our
work shows that disjoint intersection types blend in quite nicely with Cook’s denotationalmodel of inheritance.
Flattening property. In the literature of traits [Ducasse et al. 2006; Nierstrasz et al. 2006;
Schärli et al. 2003], a distinguished feature of traits is the flattening property, which says that a(non-overridden) method in a trait has the same semantics as if it were implemented directlyin the class that uses the trait. It would be interesting to see if our trait model has this property.
One problem in formulating such a property is that flattening is a property that talks about theequivalence between a flattened class (i.e., a class where all trait methods have been inlined)
and a class that reuses code from traits. Since SEDEL does not have classes, we cannot state
exactly the same property. However, we believe that one way to talk about a similar property
for SEDEL is to have something along the lines of the following example:
Example 3 (Flattening). Suppose we have m well-typed (i.e, conflict-free) traits:
trait t1 {l11 = E11, ..}, ..., trait tm {lm1 = Em1, ..}each with some number of methods, then the following two expressions are contextuallyequivalent:new (trait inherits t1 & ... & tm {})new (trait {l11 = E11,..,lm1 = Em1,..})

If we elaborate these two expressions, the property boils down to whether two merge terms(E1 , , E2 ) , , E3 and E1 , , (E2 , , E3 ) have the same semantics, which is exactly what Corollary 5.12 shows. So it is not hard to see that the above two expressions are contextually equivalent. We leave it as future work to formally state and prove flattening.

.. Type Soundness and Coherence
Since the semantics of SEDEL is defined by elaboration into F+
i it is easy to show that key
properties of F+
i are also guaranteed by SEDEL. In particular, we show that the type-directed
elaboration is type-safe in the sense that well-typed SEDEL expressions are elaborated into
well-typed F+i terms. We also show that the source language is coherent and each valid source1187.4 Formalizing Typed First-Class Traits
program has a unique (unambiguous) elaboration. We refer the reader to Appendix C for
the detailed manual proofs.
We need a meta-function | · | that translates SEDEL types to F+i types, whose definition isstraightforward. Only the translation of trait types deserves attention:|Trait [A, B]| = |A| → |B|
That is, trait types are translated to function types. | · | extends naturally to typing contexts.
Now we show several lemmas that are useful in the type-safety proof.
Lemma 7.1. If ∆ ⊢ A then |∆| ⊢ |A|.
Lemma 7.2. If A <: B then |A| <: |B|.
Lemma 7.3. If ∆ ⊢ A ∗ B then |∆| ⊢ |A| ∗ |B|.
Finally we are in a position to establish the type safety property:
Theorem 7.4 (Type-safe translation). We have that:• If ∆; Γ ⊢ T ⇒ A ⇝ E then |∆|; |Γ| ⊢ E ⇒ |A|.
• If ∆; Γ ⊢ T ⇐ A ⇝ E then |∆|; |Γ| ⊢ E ⇐ |A|.
Theorem 7.5 (Coherence). Each well-typed SEDEL expression has a unique elaboration.
Proof. By examining every elaboration rule in Fig. 7.4, it is easy to see that the elaborated F+iterm in the conclusion is uniquely determined by the elaborated F+i terms in the premises.
Then by the coherence property of F+i (Theorem 6.5), we can conclude that each well-typed
SEDEL expression has a unique unambiguous elaboration, thus SEDEL is coherent.

119 Case Study: Modularizing Language
Components
To further illustrate the applicability of SEDEL, we present a case study using object algebras [Oliveira and Cook 2012] and extensible Visitors [Oliveira 2009; Torgersen 2004]. Encodings of extensible designs for object algebras and extensible Visitors have been presented
in mainstream languages [Oliveira 2009; Oliveira and Cook 2012; Oliveira et al. 2013; Rendel
et al. 2014; Torgersen 2004]. However, prior approaches are not entirely satisfactory due tothe limitations in existing mainstream object-oriented languages. In Section 8.1, we show
how SEDEL makes those designs significantly simpler and convenient to use. In particular,
SEDEL’s encoding of extensible Visitors gives true ASTs. In Section 8.2 we show a native
support for conflict-free Object Algebra combinators, thanks to first-class traits and disjoint
polymorphism. Based on this technique, Section 8.3 gives a bird’s-eye view of several orthogonal features of a small JavaScript-like language taken from the textbook “Anatomy of Programming Languages” [Cook 2013], and illustrates how various features can be modularly
developed and composed to assemble a complete language with various operations baked in.
Section 8.4 compares our SEDEL’s implementation with that of the textbook using Haskell
in terms of lines of code.

. Object Algebras and Extensible Visitors in SEDELFirst we give a simple introduction to object algebras, a design pattern that can solve theexpression problem [Cook 1990; Krishnamurthi et al. 1998; Wadler 1998] in languages like
Java. Our starting point is the following code:
type ExpAlg[E] = {lit : Int → E,
add : E → E → E};
type IEval = { eval : Int };trait evalAlg ⇒ {121

8 Case Study: Modularizing Language Components
lit (x : Int) = {eval = x};
add (x : IEval) (y : IEval) = {eval = x.eval + y.eval}};
ExpAlg[E] is the generic interface of a simple arithmetic language with two cases, lit forliterals and add for addition. ExpAlg[E] is also referred to as an Object Algebra interface. A
concrete Object Algebra will implement such an interface by instantiating E with a suitable
type. Here we also define one operation IEval, modeled by a single-field record type. A
concrete Object Algebra that implements the evaluation rules is given by a trait evalAlg.
First-class object algebra values. The actual AST of this simple arithmetic language
is given as an internal visitor [Oliveira et al. 2008]:
type Exp = {accept : forall E . ExpAlg[E] → E};

Note that object algebras as implemented in languages like Java or Scala do not define thetype Exp because this would make adding new variants very hard, essentially due to the factthat the subtyping relation between the extended and the base languages is inverted (i.e., theextended language is a supertype of the base language). Most OO languages couple extension
with subtyping, preventing us from (easily) modeling such cases [Oliveira 2009]. Althoughextensible versions of this visitor pattern do exist, they usually require complex types using
advanced features of generics [Oliveira and Cook 2012; Torgersen 2004]. However, as we
will see, this is not a problem in SEDEL. We can build a value of Exp as follows:
e1 : Exp = {accept E f = f.add (f.lit 2) (f.lit 3)};

Adding a new operation. We add another operation IPrint to the language:
type IPrint = { print : String };trait printAlg ⇒ {lit (x : Int) = {print = x.toString1228.1 Object Algebras and Extensible Visitors in SEDEL};
add (x : IPrint) (y : IPrint) = {print = "(" ++ x.print ++ " + " ++ y.print ++ ")"}};This is done by giving another trait printAlg that implements the additional print method.
Adding a new case. A second dimension for extension is to add another case for negation:
type ExpExtAlg[E] = ExpAlg[E] & { neg : E → E };trait negEvalAlg inherits evalAlg ⇒ {neg (x : IEval) = {eval = 0 - x.eval}};
trait negPrintAlg inherits printAlg ⇒ {neg (x : IPrint) = {print= "-" ++ x.print}};

This is achieved by extending evalAlg and printAlg, implementing missing operations fornegation, respectively. We define the actual AST similarly:
type ExtExp = {accept: forall E. ExpExtAlg[E] → E};

and build a value of -(2 + 3) while reusing e1:
e2 : ExtExp = {accept E f = f.neg (e1.accept E f)};

Relations between Exp and ExpExt At this stage, it is interesting to point out an interesting subtyping relation between Exp and ExtExp: ExpExt, though being an extension of Exp
is actually a supertype of Exp. As Oliveira [2009] observed, these relations are important for
legacy and performance reasons since it means that, a value of type Exp can be automaticallyand safely coerced into a value of type ExpExt, allowing some interoperability between new
functionality and legacy code. However, to ensure type-soundness, Scala (or other common

123

8 Case Study: Modularizing Language Components
object-oriented languages) forbids any kind of type-refinement on method parameter types.
The consequence of this is that in those languages, it is impossible to express that ExtExp is
both an extension and a supertype of Exp.

. Dynamic Object Algebra Composition Support
When programming with object algebras, oftentimes it is necessary to compose multiple
operations together in such a way that they are executed in parallel to the same input. (Wealready introduced such combinator on Page 61.) For example, in the simple language wehave been developing it can be useful to create an object that supports both printing andevaluation. Oliveira and Cook [2012] addressed this problem by proposing Object Algebra
combinators that combine multiple algebras into one. However, as they noted, such combinators written in Java are difficult to use in practice, and they require significant amounts ofboilerplate. Improved variants of Object Algebra combinators have been encoded in Scalausing intersection types and an encoding of the merge operator [Oliveira et al. 2013; Rendel
et al. 2014]. However, the Scala encoding of the merge operator is quite complex as it relies
on low-level type-unsafe programming features such as dynamic proxies, reflection or othermeta-programming techniques. In SEDEL, the combination of first-class traits, dynamic
inheritance, disjoint polymorphism and nested composition allows type-safe, coherent andboilerplate-free composition of object algebras.
Abstractly speaking, what we are seeking is a combinator:
combine ∈ F [A] × F [B] → F [A & B]
That is, given two algebras with types F [A] and F [B] we want to derive, in a automatic way, athird algebra F [A & B] that combines the results of the two algebras. This combinator bears
a similarity to a zip-like operation in functional programming, and it is also directly related
to nested composition in object-oriented programming, as we have studied in Chapter 3. In
SEDEL the definition of an Object Algebra combinator is:
combine A [B * A] (f : Trait[ExpExtAlg[A]])(g : Trait[ExpExtAlg[B]]) : Trait[ExpExtAlg[A & B]] =trait inherits f & g ⇒ {};That is it. None of the boilerplate in other approaches [Oliveira and Cook 2012], or typeunsafe meta-programming techniques of other approaches [Oliveira et al. 2013; Rendel et al.
2014] are needed! Three points are worth noting: (1) combine relies on dynamic inheritance.
Notice how combine is parameterized by two traits f and g, for which their implementa-

1248.3 Case Study Overview
Types
Expressions

τePrograms
Functions
Values

pgm
decl
v

::=
::=
|
|
|
|
|
::=
::=
::=

int | booli | e1 + e2 | e1 − e2 | e1 × e2 | e1 ÷ e2
B | if e1 then e2 else e3
e1 == e2 | e1 < e2
e1 && e2 | e1 || e2
x | var x = e1 ; e2e1 e2
decl1 . . . decln efunction f(x : τ ){e}
i|B

natF
boolF
compF
logicF
varF
funcF
funcF
funcF

Figure 8.1: Mini-JS expressions, values, and types

tions are unknown statically; (2) the disjointness constraint (B * A) is crucial to ensure two
algebras (f and g) are conflict-free when being composed; (3) nested composition is the underlying mechanism to automatically derive the combined algebra by appropriately invoking(delegating) behaviors in A & B to either A or B.
To conclude, let us see combine in action. We merge the evaluation and printing algebras
to create a combined algebra expEvalPrint:
expEvalPrint = combine IEval IPrint negEvalAlg negPrintAlg;

We can use this algebra to create an object o that allows us to use evaluation and pretty printing at the same time:obj= e2.accept (IEval & IPrint)(new[ExpExtAlg[IEval & IPrint]] expEvalPrint);
main = obj.print ++ " = " ++ obj.eval.toString -- "-(2.0 + 3.0) = -5.0"

. Case Study Overview
Now we are ready to see how the same technique scales to modularize different language
features. A feature is an increment in program functionality [Lopez-Herrejon et al. 2005;
Zave 1999]. Figure 8.1 presents the syntax of a mini-JS language [Cook 2013]; each line isannotated with the corresponding feature name. Starting from a simple arithmetic language,
we gradually introduce new features and combine them with some of the existing features toform various languages. Below we briefly explain what constitutes each feature:• natF and boolF contain, among others, literals, additions and conditional expressions.
• compF and logicF introduce comparisons between numbers and logical connectives.

125

8 Case Study: Modularizing Language Components

Operations

Language
simplenat
simplebool
natbool
varbool
varnatsimplelogic
varlogicarith
arithlogic
vararithvararithlogic
mini-JS

eval

print

3
3
3
3
3
3
3
3
3
3
3
3

3
3
3
3
3
3
3
3
3
3
3
3

Data variants

check

natF
3

3

3
3

3
3
3
3
3

3
3
3
3
3

boolF

compF

logicF

3
3
3
3
3
3
3
3
3
3

3
3
3
3
3

3
3
3
3
3

varF

funcF

3
3
3
3
3
3

3

Table 8.1: Overview of the languages assembled• varF introduces local variables and variable declarations.
• funcF introduces top-level functions and function calls.
Besides, each feature is packed with 3 operations: evaluator, pretty printer and type checker.
We also define a combined algebra with the combined behavior of all the operations.
Having the feature set, we can synthesize different languages by selecting one or more
operations, and one or more data variants, as shown in Table 8.1. For example arith isa simple language of arithmetic expressions, assembled from natF, boolF and compF. On
top of that, we also define an evaluator, a pretty printer and a type checker. Note that forsome languages (e.g., simplenat), since they have only one kind of value, we only define anevaluator and a pretty printer. We thus obtain 12 languages and 30 operations in total. The
complete language mini-JS contains all the features and supports all the operations. The
reader can refer to our supplementary material for the source code of the case study.

. Evaluation
To evaluate SEDEL’s implementation of the case study, Table 8.2 compares the number ofsource lines of code (SLOC, lines of code without counting empty lines and comments) for
SEDEL’s modular implementation with the vanilla non-modular AST-based implementations in Haskell. The Haskell implementations are just straightforward AST interpreters,
which duplicate code across the multiple language components.
Since SEDEL is a new language, we had to write various code that is provided in Haskell
by the standard library, so they are not counted for fairness of comparison. In the left part,126

8.4 Evaluation

Feature

eval

print

check

Lang

natF(7)
boolF(4)compF(4)logicF(4)varF(4)funcF(3)

23
9
12
12
7
10

7
4
4
4
4
3

39
17
20
20
7
9

simplenat
simplebool
natbool
varbool
varnatsimplelogic
varlogicarith
arithlogic
vararithvararithlogic
mini-JS

Total

237

SEDEL

Haskell% Reduced

3
3
5
4
4
4
6
8
8
8
8
33

33
16
74
24
41
28
36
94
114
107
127
149

91%
81%
93%
83%
90%
86%
83%
91%
93%
93%
94%
78%

331

843

61%

Table 8.2: SLOC statistics: SEDEL implementation vs. vanilla AST implementationfor each feature, we count the lines of the algebra interface (number beside the feature name),
and the algebras for the operations. In the right part, for each language, we count the linesof ASTs, and those to combine previously defined operations. For example, here is the codethat is needed to make the arith language.
-- Object Algebra interface
type ArithAlg[E] = NatBoolAlg[E] & CompAlg[E];-- AST
type Arith = {accept : forall E. ArithAlg[E] → E};-- Evaluator
evalArith (e : Arith) : IEval =e.accept IEval(new[ArithAlg[IEval]] evalNatAlg & evalBoolAlg & evalCompAlg);-- Pretty printerppArith (e : Arith) : IPrint =e.accept IPrint(new[ArithAlg[IPrint]] ppNatAlg & ppBoolAlg & ppCompAlg);-- Type checker
tcArith (e : Arith) =e.accept ITC (new[ArithAlg[ITC]] tcNatAlg & tcBoolAlg & tcCompAlg);

We only need 12 lines in total: 4 lines for the AST, and 8 lines to combine the operations.
Therefore, the total SLOC of SEDEL’s implementation is the sum of all the lines in the feature and language parts (237 SLOC of all features plus 94 SLOC of ASTs and operations). Al-

127

8 Case Study: Modularizing Language Components
though SEDEL is considerably more verbose than a functional language like Haskell, SEDEL’s
modular implementation for 12 languages and 30 operations in total reduces approximately
60% in terms of SLOC. The reason is that, the more frequently a feature is reused by otherlanguages directly or indirectly, the more reduction we see in the total SLOC. For example,
natF is used across many languages. Even though simplenat itself alone has more SLOC
(40 = 7 + 23 + 7 + 3) than that of Haskell (which has 33), we still get a huge gain when
implementing other languages.
Final remarks. We acknowledge that there are several limitations in our case study. On
the one hand, SLOC is just one metric and we have not yet measured any other metrics.
On the other hand, we did not compare with other modular approaches such as finally tagless [Carette et al. 2009]. Nevertheless we believe that the case study is already non-trivial inthat we need to solve the expression problem. Note that Scala traits alone are not sufficienton their own to solve the expression problem. While there are solutions in both Haskell and
Scala, they introduce significant complexity, as explained in Section 8.1.

128

Part V
Related and Future Work129

 Related Work
There is a great deal of work related to this thesis. We have touched some most relevant work
(notably intersection types) in Chapter 2. In this chapter, we briefly review other related
work, starting with a summary of two most common approaches on coherence (Section 9.1).
We then consider various existing mechanisms to foster modularity and code reuse in the restof this chapter.

. Coherence
In calculi that feature coercive subtyping, a semantics that interprets the subtyping judgment
by introducing explicit coercions is typically defined on typing derivations rather than on
typing judgments. A natural question that arises for such systems is whether the semantics
is coherent, i.e., distinct typing derivations of the same typing judgment possess the same
meaning. Since Reynolds [1991] proved the coherence of a calculus with intersection types,
based on the denotational semantics for intersection types, many researchers have studied
the problem of coherence in a variety of typed calculi. Below we summarize two commonlyfound approaches in the literature.

.. Normalization-based Approach
The first approach is based on normalization. Breazu-Tannen et al. [1991] proved the coherence of a coercion translation from Fun [Cardelli and Wegner 1985] extended with recursive
types to System F by showing that any two typing derivations of the same judgment are normalizable to a unique normal derivation where the correctness of the normalization steps isjustified by an equational theory in System F. Curien and Ghelli [1992] presented a translation of System F≤ into a calculus with explicit coercions and showed that any derivations of
the same judgment are translated to terms that are normalizable to a unique normal form.
Following the same approach, Schwinghammer [2008] proved the coherence of coercion
translation from Moggi’s computational lambda calculus [Moggi 1991] with subtyping.

131

9 Related Work.. Context-based Approach
Central to the first approach is to find a normal form for a representation of the derivationand show that normal forms are unique for a given typing judgment. However, this approach cannot be directly applied to Curry-style calculi, i.e., where the lambda abstractions
are not type annotated. Also this line of reasoning cannot be used when the calculus has
general recursion. Biernacki and Polesiuk [2015] considered the coherence problem of coercion semantics. Their criterion for coherence of the translation is contextual equivalencein the target calculus. They presented a construction of logical relations for establishing so
constructed coherence for coercion semantics, showing that this approach is applicable in avariety of calculi, including delimited continuations and control-effect subtyping.
Inspired by this approach, Bi et al. [2018] proposed the canonicity relation to prove coherence for a calculus with disjoint intersection types and BCD subtyping. BCD subtypingin our setting poses a non-trivial complication over Biernacki and Polesiuk’s simple structural subtyping. Indeed, because any two coercions between given types are behaviorally
equivalent in the target language, their coherence reasoning can all take place in the target
language. This is not true in our setting, where coercions can be distinguished by arbitrarytarget programs, but not those that are elaborations of source programs. (Recall that λx. π1 xand λx. π2 x should be equated in our setting.) Hence, we have to restrict our reasoning to
the latter class, which is reflected in a more complicated notion of contextual equivalenceand our logical relation’s non-trivial treatment of pairs. As we have shown in Chapter 6,
constructing a suitable logical relation for F+
i is challenging. On the one hand, the original
approach by Alpuim et al. [2017] in Fi does not work any more due to the addition of BCD
subtyping. On the other hand, simply combining System F’s logical relation with λ+i ’s canonicity relation does not work as expected, due to the issue of well-foundedness. To solve theproblem, we employ immediate substitutions and a restriction to predicative instantiations.

. BCD Subtyping and Decidability
The BCD type system was first introduced by Barendregt et al. [1983]. It is derived froma filter lambda model in order to characterize exactly the strongly normalizing terms. The
BCD type system features a powerful subtyping relation, which serves as a base for our subtyping relation. Bessai et al. [2014] show how to type classes and mixins in a BCD-style
record calculus with a merge-like operator [Bracha and Cook 1990] that only operates onrecords, and they only study a type assignment system. The decidability of BCD subtyping has been shown in several works [Kurata and Takahashi 1995; Pierce 1989; Rehof and
Urzyczyn 2011; Statman 2015]. Laurent [2012a] formalized the relation in Coq in order to132

9.2 BCD Subtyping and Decidability
eliminate transitivity cuts from it, but his formalization does not deliver an algorithm. Only
recently, Laurent [2018] presents a general way of defining a BCD-like subtyping relation extended with generic contravariant/covariant type constructors that enjoys the “sub-formulaproperty” (read decidability). The key idea is to generalize the form of subtyping from A <: B
to A1 , . . . , An ⊢ B, which is interpreted as meaning A1 & ... & An <: B. Here is his subtyping system instantiated with singleton records, adapted to our setting:Int ⊢ Int

⊢⊤

⊢B

⊢A

Γ, ∆ ⊢ CΓ, ∆ ⊢ C⊢A→B⊢ {l : A}

Γ, Int, ∆ ⊢ CΓ, ⊤, ∆ ⊢ CΓ, ∆ ⊢ CΓ, ∆ ⊢ C

Γ, A → B, ∆ ⊢ C

Γ, {l : B}, ∆ ⊢ C

A ⊢ A1...

A ⊢ AnΓ⊢AΓ⊢BΓ ⊢ A&B

B1 , . . . , Bn ⊢ B

A1 → B1 , . . . , An → Bn ⊢ A → B

Γ, A, B, ∆ ⊢ C
Γ, A & B, ∆ ⊢ C

A1 , . . . , An ⊢ B{l : A1 }, . . . , {l : An } ⊢ {l : B}

The first two rules are the base cases. The third and forth rules deal with cases where B is a
“top-like” type. The next four rules are the weakening rules for integers, top types, function
types and singleton records. The next two rules are the introduction and elimination rules forintersections. The last two rules combine the function distributivity rule with usual function
subtyping, and record distributivity rule with usual record subtyping, respectively. Laurent
proved in Coq that A ⊢ B if and only if A <: B. Our Coq formalization follows a different
idea based on Pierce’s decision procedure [Pierce 1989], which is shown to be easily extensible to records, parametric (disjoint) polymorphism and corresponding distributivity rules.
In the course of our mechanization we identified several mistakes in Pierce’s proofs, as well
as some important missing lemmas. More recently, Muehlboeck and Tate [2018] presented
a decidable algorithmic system (proved in Coq) with union and intersection types. Similar
to F+
i , their system also has distributive subtyping rules. They also discussed the additionof polymorphism, but left a Coq formalization for future work. In their work they regard
intersections of disjoint types (e.g., String & Int) as uninhabitable, which is different from
our interpretation. As a consequence, coherence is a non-issue for them. Finally, it would
be interesting to study an efficient subtyping algorithm in normal practice. As noted by
Reynolds [1997], however, the worst-case inefficiency is inevitable. In fact, any typechecker
for languages using intersection types is PSPACE-hard.

133

9 Related Work

. Intersection types, Merge Operator and Polymorphism
Forsythe [Reynolds 1988] has intersection types and a merge-like operator. However to ensure coherence, various restrictions were added to limit the use of merges. Forsythe only
permits p1 , , p2 when p2 is either an lambda abstraction or a record, whose meaning “overrides” the corresponding type of meaning of p1 . For instance, there is a rule regarding lambda
abstraction that says (adapted to our syntax):
Γ ⊢ λx. p2 : θ1 → θ2
Γ ⊢ (p1 , , λx. p2 ) : θ1 → θ2
which means that in a merge of two functions, the second one always takes precedence to thefirst one. In contrast, our typing rule for merges is more fine-grained in the sense that both
functions are retained as long as they are disjoint. Castagna et al. [1992] proposed a coherent
calculus λ& to study overloaded functions. λ& has a special merge operator that works onfunctions only. Dunfield [2014] proposed a calculus (which we call λ,, ) that shows significant expressiveness of type systems with unrestricted intersection types and a (unrestricted)
merge operator. However, because of his unrestricted merge operator (allowing 1 , , 2), his
calculus lacks coherence. Blaauwbroek [2017]’s λ∨∧ enriched λ,, with BCD subtyping andcomputational effects, but he did not address coherence. The coherence issue for a calculus
similar to λ,, was first addressed in λi [Oliveira et al. 2016] with the notion of disjointness,but at the cost of dropping unrestricted intersections, and a strict notion of coherence (basedon α-equivalence). Later Bi et al. [2018] improved calculi with disjoint intersection types byremoving several restrictions, adopted BCD subtyping and a semantic notion of coherence(based on contextual equivalence) proved using canonicity. The combination of intersection
types, a merge operator and parametric polymorphism, while achieving coherence was firststudied in Fi [Alpuim et al. 2017], which serves as a foundation for F+
i . However, Fi suffered
the same problems as λi . Additionally in Fi a bottom type is problematic due to interactions
with disjoint polymorphism and the lack of unrestricted intersections. The issues can be illustrated with the well-typed F+
i expression Λ(α ∗ ⊥). λx : α. x , , x. In this expression thetype of x , , x is α & α. Such a merge does not violate disjointness because the only types that
α can be instantiated with are top-like, and top-like types do not introduce incoherence. In
Fi a type variable α can never be disjoint to another type that contains α, but (as the previous expression shows) the addition of a bottom type allows expressions where such (strict)condition does not hold. In F+
i , we removed those restrictions, extended BCD subtyping
with polymorphism, and proposed a more powerful logical relation for proving coherence.

1349.4 Row polymorphism and bounded polymorphism

λ,,

λi

λ∨
∧

λ+
i

Fi

F+
i

Disjointness
Unrestricted intersections
BCD subtyping
Polymorphism
Coherence
Bottom typeFigure 9.1: Summary of intersection calculi

Figure 9.1 summarizes the main differences between the aforementioned calculi, where =yes, = no, and = syntactic coherence.
There are also several other calculi with intersections and polymorphism. Pierce proposed F∧ [Pierce 1991], a calculus combining intersection types and bounded quantification. Pierce translates F∧ to System F extended with products, but he left coherence as aconjecture. More recently, Castagna et al. [2014] proposed a polymorphic calculus with settheoretic type connectives (intersections, unions, negations). But their calculus does notinclude a merge operator. Castagna and Lanvin [2017] also proposed a gradual type systemwith intersection and union types, but also without a merge operator.

. Row polymorphism and bounded polymorphism
Row polymorphism was originally proposed by Wand [1987] as a mechanism to enable type
inference for a simple object-oriented language based on recursive records. These ideas werelater adopted into type systems for extensible records [Gaster and Jones 1996; Harper andPierce 1991; Leijen 2005]. Our merge operator can be seen as a generalization of record
extension/concatenation, and selection is also built-in. In contrast to most record calculi,+
restriction is not a primitive operation in λ+i and Fi , but can be simulated via subtyping.

Disjoint quantification can simulate the lacks predicate often present in systems with row
polymorphism. Recently Morris and McKinna [2019] presented a typed language, generalizing and abstracting existing systems of row types and row polymorphism. Alpuim et al.
[2017] informally studied the relationship between row polymorphism and disjoint polymorphism, but it would be interesting to study such relationship more formally. The work of
Morris and McKinna may be interesting for such study in that it gives a general framework
for row type systems.
Bounded quantification is currently the dominant mechanism in major mainstream objectoriented languages supporting both subtyping and polymorphism. F<: [Cardelli and Weg-

135

9 Related Workner 1985] provides a simple model for bounded quantification, but type-checking in full F<:
is proved to be undecidable [Pierce 1994]. Pierce’s thesis [Pierce 1991] discussed the relationship between calculi with simple polymorphism and intersection types and bounded
quantification. He observed that there is a way to “encode” many forms of bounded quantification in a system with intersections and pure (unbounded) second-order polymorphism.
That encoding can be easily adapted to F+i :∀(α <: A). B ≜ ∀(α ∗ ⊤). [A & α/α]BThe idea is to replace bounded quantification by (unrestricted) universal quantification andall occurrences of α by A & α in the body. Such an encoding seems to indicate that F+
i could
be used as a decidable alternative to (full) F<: . It is worthwhile to note that this encoding
does not work in Fi because A & α is not well-formed (α is not disjoint to A). In other words,
the encoding requires unrestricted intersections.

. Typed First-Class Classes/Mixins/Traits
First-class classes have been used in Racket [Flatt et al. 2006], along with mixin support, andhave shown great practical value. For example, DrRacket IDE [Findler et al. 2002] makes
extensive use of layered combinations of mixins to implement text editing features. The topic
of first-class classes with static typing has been explored by Takikawa et al. [2012] in Typed
Racket. They designed a gradual type system that supports first-class classes. Of particularinterest is their use of row polymorphism to type mixins. For example, modal_mixin from
Section 7.2 implemented in Typed Racket has type:(All (r / on-key toggle-mode)(Class ([on-key : (String → Void)] | r)) →(Class ([toggle-mode : (→ Void)] [on-key : (String → Void)] | r)))As with our use of disjoint polymorphism, row polymorphism can express constraints on
the presence or absence of members. As a consequence, Typed Racket ends up with two
subtyping mechanisms: one for first-class classes (via row polymorphism) and the other forobjects (via normal width subtyping). In contrast, SEDEL uses only one mechanism—i.e.,
disjoint polymorphism—to deal with both.
More recently, Lee et al. [2015] proposed a model for typed first-class classes based ontagged objects. Like our development, the semantics of their source language is defined by atranslation into a target language. One notable difference to SEDEL is that they require theuse of a variable rather than an expression in the extends clause, whereas we do not have this136

9.6 Mixin-Based Inheritance
restriction. In their source language, subclasses define subtypes, which limits its applicability
to extensible designs. Also their target calculus is significantly more complex than ours dueto the use of dependent function types and dependent sum types. As they admitted, theyomit inheritance in their formalization.
Racket also supports a dynamically typed model of first-class traits [Flatt et al. 2006]. However, unlike Racket’s first-class classes and mixins, there is no type system supporting the useof first-class traits. A key difficulty is statically detecting conflicts. In the mixin model thisis not a problem because conflicts are implicitly resolved using the order of composition. As
far as we know, SEDEL is the first design for typed first-class traits.

. Mixin-Based InheritanceBracha and Cook’s seminal paper [Bracha and Cook 1990] extends Modula-3 with mixins.
Since then, many mixin-based models have been proposed [Ancona et al. 2003; Bono et al.
1999; Flatt et al. 1998]. Mixin-based inheritance requires that mixins are composed linearly,
and as such, conflicts are resolved implicitly. In comparison, the trait model in SEDEL requires conflicts to be resolved explicitly. Note that conflict detection is essential in expressing
composition operators for object algebras, without running into ambiguities. Bracha’s Jigsaw framework [Bracha 1992] formalized mixin composition, along with a rich trait algebra
including merge, restrict, select, project, overriding and rename operators. Lagorio et al.
[2012] proposed FJig that reformulates Jigsaw constructs in a Java-like setting. Allen et al.
[2003] described how to add first-class generic types—including mixins—to object-oriented
languages with nominal typing. Corradi et al. [2012] described an extension of FJig that integrates modular composition and nesting of Java-like classes. It features a set of composition
operators that allow to manipulate nested classes at any depth level. In all of these systems,
classes and mixins, though they enjoy static typing, are still second-class constructs, and thus
their systems cannot express dynamic inheritance.

. Trait-Based Inheritance
Traits were originally proposed by Schärli et al. [2003], and later formalized by Ducasse
et al. [2006] as a mechanism for fine-grained code reuse to overcome many limitations ofclass-based inheritance. The original proposal of traits were implemented in the dynamically typed class-based language Squeak/Smalltalk. Since then various formalizations oftraits in a Java-like (statically typed) setting have been proposed [Fisher and Reppy 2004;
Nierstrasz et al. 2006; Scharli et al. 2003; Smith and Drossopoulou 2005]. In most of the1379 Related Work
above proposals, trait composition complements class-based inheritance. SEDEL, in thespirit of pure trait-based programming languages [Bettini and Damiani 2017; Bettini et al.
2013b], embraces traits as the sole mechanism for code reuse. The deviation from traditional class-based inheritance is not only because of its simplicity, but also because we needa very dynamic form of inheritance. In comparison to the traditional trait mode, traits in
SEDEL have the following differences:
1. traditional traits cannot be instantiated but only composed with a class, whereas traits
in SEDEL can be instantiated directly;
2. traditional traits cannot take constructor parameters whereas ours can;3. the trait system in SEDEL lacks a proper notion of inheritance relationship, e.g., in the
traditional trait model, if the same method is obtained more than once via different
paths, there is no conflict. This is not the case in SEDEL;
4. and finally traits in SEDEL are first-class and support dynamic inheritance.

. Family Polymorphism
There has been much work on family polymorphism since Ernst’s original proposal [Ernst2001]. Family polymorphism provides an elegant solution to the expression problem. Although a simple Scala solution does exist without requiring family polymorphism (e.g., see
Wang and Oliveira [2016]), Scala does not support nested composition: programmers needto manually compose all the classes from multiple extensions. Generally speaking, systems
that support family polymorphism can be divided into two categories: those that support object families and those that support class families. The original object family approach of Beta
(e.g., virtual classes [Madsen and Moller-Pedersen 1989]) treats nested classes as attributesof objects of the family classes, whereas in class families, classes are nested in other classes.
The former choice is considered more expressive [Ernst et al. 2006], but requires a complex
type system usually involving dependent types. The object and class family approaches haveeven been combined by the work on Tribe [Clarke et al. 2007].
Object families. Virtual classes [Madsen and Moller-Pedersen 1989] as introduced in
Beta [Lehrmann Madsen et al. 1993] are based on object families. However, the virtual
class mechanism in Beta is known to be unsound [Bruce et al. 1998]. Path-dependent types
are used to ensure type safety for virtual types and virtual classes in the calculus vc [Ernst
et al. 2006]. One distinctive difference from our calculi is that vc follows the mixin-style by138

9.9 Languages with More Advanced Forms of Inheritanceallowing the rightmost class to take precedence, whereas in λ+i conflicts are detected staticallyand resolved explicitly.
Class families. Concord [Jolly et al. 2004], Jx [Nystrom et al. 2004] and J& [Nystrom et al.
2006] follow the class family approach, where nested classes and types are attributes of thefamily classes directly. Jx supports nested inheritance, a class family mechanism that allows
nesting of arbitrary depth. J& is a language that supports nested intersection, building on topof Jx. Similar to our calculi, intersection types play an important role in J&, which are used tocompose packages/classes. However, J& does not have a merge-like operator. When conflicts
arise, prefix types can be exploited to resolve the ambiguity. J&s [Qi and Myers 2009] is anextension of the Java language that adds class sharing to J&. Saito et al. [2007] identified aminimal, lightweight set of language features to enable family polymorphism,
Compared with those systems, which usually focus on getting a relatively complex Javalike language with family polymorphism, our work on λ+i focuses on a minimal calculusthat supports nested composition. We have shown that a calculus with the merge operator
and a variant of BCD subtyping captures the essence of nested composition. Moreover, λ+ienables new insights on the subtyping relations of families. Our goal in this thesis is not tosupport full family polymorphism which, besides nested composition, also requires dealing
with other features such as self types [Bruce et al. 1995; Saito and Igarashi 2009] and mutablestate. But we expect to investigate those features in the future.

. Languages with More Advanced Forms of Inheritance
Self [Ungar and Smith 1988] is a dynamically typed, prototype-based language with a simple and uniform object model. Self’s inheritance model is typical of what we call mutable
inheritance, because an object’s parent slot may be assigned new values at run time. Mutable
inheritance is rather unstructured, and oftentimes access to any clashing methods will generate a “messageAmbiguous” error at run time. Although SEDEL’s dynamic inheritance is not
as powerful as mutable inheritance, its static type system can guarantee that no such errors
occur at run time. Eiffel [Meyer 1987] supports a sophisticated class-based multiple inheritance with deep renaming, exclusion and repeated inheritance. Of particular interest is thatin Eiffel, name collisions are considered programming errors, and ambiguities must be resolved explicitly by the programmer (by means of renaming). In this regard, SEDEL is quite
like Eiffel. However, the type system in SEDEL is more lenient in that two identically named
methods with different signatures can coexist. Grace [Jones et al. 2016; Noble et al. 2017]is an object-based language designed for education, where objects are created by object con-139

9 Related Workstructors. Since Grace has mutable fields, it has to consider many concerns when it comes toinheritance, resulting in a rather complex inheritance mechanism with various restrictions.
Since SEDEL is pure, a relatively simple encoding of traits with late binding of self suffices
for our applications. Grace’s support for multiple inheritance is based on what they call instantiable traits. We believe that there is plenty to be learned from Grace’s design of traits if wewant to extend our trait model with features such as mutable state. MetaFJig [Servetto and
Zucca 2014] (an extension of FJig) supports dynamic trait replacement [Bettini et al. 2013a;
Ducasse et al. 2006; Smith and Drossopoulou 2005], a feature for changing the behavior ofan object at run time by replacing one trait for another. More recently, a Java-like language
called Familia [Zhang and Myers 2017] were proposed to combine subtyping polymorphism,
parametric polymorphism and family polymorphism.

. Module Systems
In parallel to OOP, the ML module system originally proposed by MacQueen [1984] alsooffers powerful support for flexible program construction. There is a large body of work on
ML modules. Supporting data abstraction is the primary focus of the module mechanism in
ML. It ensures implementer-side data abstraction by allowing the implementer of a module to“hide” specific implementation behind an abstract interface. It also supports a form of clientside data abstraction where a client can develop and compile a module independently fromthe modules on which it depends, via the “functor” mechanism. One major limitation of the
traditional ML module systems is the lack of support for mutually recursive modules. There
are several proposals of extending ML with recursive modules [Crary et al. 1999; Rossberg
and Dreyer 2013; Russo 2001]. Mixin modules in the Jigsaw framework [Bracha and Lindstrom 1992] provides a suite of operators for adapting and combining modules. The MixML
module system [Rossberg and Dreyer 2013] incorporates mixin module composition, while
retaining the full expressive powerful of ML modules. There is also work on elaborating thesemantics of module systems into a smaller, well-established internal language. Rossberg
et al. [2014] showed that plain System F is sufficient as an internal language for conventional
ML modules. Furthermore, Rossberg [2015] proposed a redesign of ML in which modules
are truly first-class values, thus unifying the core and module layers into one language.
Module systems usually put more emphasis on supporting data abstraction. Support fordata abstraction adds considerable complexity, which is not needed in SEDEL. SEDEL is focused on OOP and supports, among others, method overriding, self references and dynamic
dispatching, which (generally speaking) are all missing features in module systems.

140 Future WorkIn this section we discuss some areas where future research might extend and/or complement
the work described in this thesis.

. Categorical Semantics
An interesting avenue for future work is to give a categorical semantics of disjoint intersection
types. The main reason for doing so is that, as Reynolds [1988] nicely put it:“by formulating succinct definitions in terms of a mathematical theory of great
generality, we gain an assurance that our language will be uniform and general.”Using category theory as the basis for the type structure of a programming language has along history. Lambek [1985] discovered that simply-typed lambda calculus can be interpreted in any Cartesian closed category. Reynolds [1991] gives a category-theoretic presentation of a lambda calculus extended to include records, fixed points and intersection types,
much similar to our λ+i . Of particular interest to us is his method for proving coherence.
Let D denote derivations of typing, then the interpretation of a derivation D :: Γ ⊢ E : A
is a morphism JD :: Γ ⊢ E : AK : JΓK → JAK in a suitable “semantic” category (i.e., being
Cartesian closed and possessing certain limits). Proving coherence in this presentation thenamounts to establishing the commutativity of all diagrams of the following form:1
JD1 ::Γ⊢E:AK

JΓK

JAK
JD2 ::Γ⊢E:AK

.. Properties of Intersection Types
The key component of Reynolds’ method is the interpretation of intersection types. For thesake of precision in what follows, we pause to give some basic properties of intersection types
1

The proof actually needs a stronger inductive hypothesis.

141

10 Future Workthat are first proved by Reynolds [1991]. First we give two definitions that are important forthe discussion.
Definition 16 (Type Equivalence). Two types A and B are equivalent, written A ≈ B, whenA <: B and B <: A.
Definition 17 (Least Upper Bounds). A least upper bound of A and B is a supertype of both
A and B and a subtype of every common supertype of A and B—i.e., a type C such that:• A <: C• B <: C• For any C′ , A <: C′ and B <: C′ implies C <: C′
According to the subtyping rules in Fig. 3.3, we can derive the following type equalities:
Proposition 10.1.
A1 & (A2 & A3 ) ≈ (A1 & A2 ) & A3⊤&A ≈ AA&⊤ ≈ A
A1 & A2 ≈ A2 & A1
A&A ≈ A{l : A1 & A2 } ≈ {l : A1 } & {l : A2 }A → A1 & A2 ≈ (A → A1 ) & (A → A2 ){l : ⊤} ≈ ⊤A→⊤≈⊤Furthermore, it can be shown that every pair of λ+i types has a least upper bound (unique
up to ≈-equivalence). The following meta-function yields a least upper bound (written A⊔B)
for any types A and B:

142

10.1 Categorical Semantics
Proposition 10.2.
A⊔B=B⊔AA⊔⊤=⊤
A1 ⊔ (A2 & A3 ) = (A1 ⊔ A2 ) & (A1 ⊔ A3 )Int ⊔ {l : A} = ⊤Int ⊔ (A1 → A2 ) = ⊤{l : A} ⊔ (A1 → A2 ) = ⊤{l : A1 } ⊔ {l : A2 } = {l : A1 ⊔ A2 }{l1 : A1 } ⊔ {l2 : A2 } = ⊤ when l1 ̸= l2(A1 → A′1 ) ⊔ (A2 → A′2 ) = (A1 & A2 ) → (A′1 ⊔ A′2 ).. Connecting with Disjointness
With the above propositions stated, it turns out that our disjointness rules, as given in Fig. 3.5,
can be compactly formulated using ≈ and ⊔:Theorem 10.3. A ∗ B if and only if A ⊔ B ≈ ⊤.
Proof. By induction on the derivation of disjointness. An interesting case is rule D-arr
D-arr

A2 ∗ B2
A1 → A2 ∗ B1 → B2

A2 ⊔ B 2 ≈ ⊤By i.h(A1 → A2 ) ⊔ (B1 → B2 ) ≈ (A1 & B1 ) → (A2 ⊔ B2 )By Proposition 10.2

(A1 → A2 ) ⊔ (B1 → B2 ) ≈ (A1 & B1 ) → ⊤By above equality(A1 & B1 ) → ⊤ ≈ ⊤By Proposition 10.1

(A1 → A2 ) ⊔ (B1 → B2 ) ≈ ⊤By above equality

Remark. We can view Theorem 10.3 as a specification of disjointness. Moreover, it provides
an alternative approach to deriving algorithmic disjointness whenever A ⊔ B is computable.
However, this is not always the case for richer type structures. For instance, in the F∧ calculus [Pierce 1991], least upper bounds are not existent.

143

10 Future Work.. Interpretation of Intersection Types
Following Reynolds, a subtyping derivation is interpreted as a morphism JA <: BK : JAK →JBK with two requirements:
1. For all types A the morphism from JAK to JAK must be an identity arrow.
2. Whenever A <: B and B <: C, the composition of JA <: BK and JB <: CK must
be equal to JA <: CK, i.e., JA <: BK; JB <: CK = JA <: CK. (Here “;” denotes
composition in diagrammatic order.)These requirements actually make J·K a functor from the preordered set of types (viewed asa category) to the semantic category of choice.
Remark. By definition, whenever A ≈ B we say JAK is isomorphic to JBK, written JAK ∼= JBK.
Now we consider JA1 & A2 K in the following steps:
1. By rules S-andL and S-andR, there must be two morphisms, π1 : JA1 & A2 K → JA1 K
and π2 : JA1 & A2 K → JA2 K
JA1 K

JA2 K
π1

π2

JA1 & A2 K
2. For any types A1 and A2 , there exists a least upper bound A1 ⊔ A2 (Proposition 10.2),
and two morphisms JA1 <: A1 ⊔ A2 K : JA1 K → JA1 ⊔ A2 K and JA2 <: A1 ⊔ A2 K :JA2 K → JA1 ⊔ A2 K, and the following diagram should commute:
JA1 ⊔ A2 K
JA1 <:A1 ⊔A2 K

JA2 <:A1 ⊔A2 K

JA1 K

JA2 K
π1

π2

JA1 & A2 K

144

10.1 Categorical Semantics
3. For every type A such that A <: A1 and A <: A2 , rule S-and implies that A <:A1 & A2 , thus a morphism from JAK to JA1 & A2 K. Call this µ0 . The following diagram
should commute:
JA1 ⊔ A2 K
JA1 <:A1 ⊔A2 K

JA2 <:A1 ⊔A2 K

JA1 K

JA2 K
π1

π2

JA1 & A2 K

JA<:A1 K

JA<:A2 Kµ0

JAK4. Furthermore, in the above diagram, we replace JAK by an arbitrary object s and JA <:A1 K and JA <: A1 K by any morphisms f1 and f2 that make the outer diamond commutes, and we require the “mediating morphism” µ0 from s to JA1 & A2 K to be unique.
Specifically, we define JA1 & A2 K by requiring the following diagram must commute:
JA1 ⊔ A2 K
JA1 <:A1 ⊔A2 K

JA2 <:A1 ⊔A2 K

JA1 K

JA2 K
π1

f1

π2

JA1 & A2 K

f2

µ0

s
Thus we have defined JA1 & A2 K to be the pullback of JA1 K, JA2 K and JA1 ⊔ A2 K.

.. Interpretation of Disjoint Intersection Types
Given the interpretation of intersection types, it is fairly straightforward to give the interpretation of disjoint intersection types. First recall that if A ∗ B then A ⊔ B ≈ ⊤ (Theorem 10.3).
Also we have J⊤K = 1—i.e., the terminal object. By specializing JA1 ⊔ A2 K to be the terminal
object (JA1 <: A1 ⊔ A2 K and JA2 <: A1 ⊔ A2 K are then uniquely determined), then the
pullback “degenerates” to the product of JA1 K and JA2 K. In other words, the interpretationof disjoint intersection types is given by the following theorem:

145

10 Future Work
Theorem 10.4. If A1 ∗ A2 then JA1 & A2 K ∼= JA1 K × JA2 K.
Remark. It is reassuring to see that this theorem justifies our translation of disjoint intersection types into product types, from the categorical perspective.

.. Coherence, from the Categorical Perspective
What we have developed so far is the (categorical) interpretation of disjoint intersection
types. We are still half way through the ultimate goal of (re-)establishing coherence, now
from the categorical perspective. The main difficulty is that we do not know yet how to interpret bidirectional typing judgments—i.e., what are JΓ ⊢ E ⇒ AK and JΓ ⊢ E ⇐ AK,
and in particular the interpretation of the merge operator. As remarked earlier, bidirectional
type checking (besides disjointness) is essential to coherence. It would be exciting to see someresearch along the lines of the above, so that we may have a solid mathematical foundation
for type systems with disjoint intersection types.

. Implicit Polymorphism
Another interesting and practically useful extension is to study (predicative) implicit polymorphism, in the spirit of languages like Haskell or ML. Our F+
i calculus features explicit
polymorphism in the sense that we need to provide types during type applications. A classic
example of implicit polymorphism is the identity function λx. x of type ∀α. α → α. Whenapplied to 1, for example, the type variable α will be implicitly instantiated to Int. Moreover, we are interested in higher-rank polymorphism, allowing polymorphic quantifiers toappear anywhere in a type. There are several approaches in the literature [Dunfield and Krishnaswami 2013; Odersky and Läufer 1996; Peyton Jones et al. 2007]. Since our declarative
type system is already based on bidirectional type-checking, the work by Dunfield and Krishnaswami [2013] is particularly relevant for us. It turns out that coming up with a coherent
declarative system is already very challenging, especially the disjointness relation. Below wesketch out some ideas for the initial design.

.. Declarative Subtyping
First we consider the subtyping rules. Obviously rule S-forall needs to be modified. We
replace it with the following two rules:
IS-allL∆ ⊢ t ∗ A1

IS-allR∆ ⊢ [t/α]A2 <: B∆ ⊢ ∀(α ∗ A1 ). A2 <: B146∆, α ∗ B1 ⊢ A <: B2∆ ⊢ A <: ∀(α ∗ B1 ). B2

10.2 Implicit Polymorphism
Rule IS-allL says that a type ∀(α ∗ A1 ). A2 is a subtype of B if some instantiation [t/α]A2 is
a subtype of B. However, unlike Dunfield and Krishnaswami’s system, in our setting, not all
monotypes t that make the subtyping go through are equally fine—those that do not respect
the disjointness constraints should not be considered, for the sake of coherence. Otherwise,
we would allow ((λx. x , , 2) : ∀(α ∗ Int). α → α & Int) 1 to type check, which would cause
ambiguity at run time. Rule IS-allR says that A is a subtype of ∀(α ∗ B1 ). B2 if we canshow that A is a subtype of B2 in a context extended with α ∗ B1 . It is not immediately
obvious that these two rules subsume rule S-forall, and in particular what happens to “auniversal quantifier is contravariant in its disjointness constraints”, which is very importantin the original subtyping. It can be shown that they do subsume rule S-forall, as exhibitedby the following derivation:
A2 <: A1
α ∗ A2 ⊢ α ∗ A1

FD-tvarLα ∗ A2 ⊢ B1 <: B2

α ∗ A2 ⊢ ∀(α ∗ A1 ). B1 <: B2

IS-allL• ⊢ ∀(α ∗ A1 ). B1 <: ∀(α ∗ A2 ). B2

IS-allR.. Disjointness
The disjointness relation needs a major overhaul. For instance, subtyping allows ∀(α ∗Char). α → α <: Int → Int, and as such, ∀(α ∗ Char). α → α is no longer disjoint with
Int → Int, whereas ∀(α ∗ Int). α → α is disjoint with Int → Int. A seemingly intuitive rule
is as follows:
FD-implicit∆ ⊢ t1 ∗ A1

∆ ⊢ [t1 /α]A2 ∗ B2

∆ ⊢ ∀(α ∗ A1 ). A2 ∗ B2
In the above rule, the monotype t1 is existentially quantified: it suffices to exhibit a disjointness derivation of [t1 /α]A2 and B2 for one monotype in order to build a disjointness
derivation of ∀(α ∗ A1 ). A2 and B2 . Unfortunately, this rule is incorrect as we could guess a“wrong” t1 . Take ∀(α ∗ Char). α → α for example: one instantiation is Bool → Bool, which
is disjoint with Int → Int. But as we saw, this does not mean ∀(α ∗ Char). α → α is disjoint
with Int → Int. Instead we should require all possible instantiations are disjoint with B2 :FD-implicit∀t1 . ∆ ⊢ t1 ∗ A1 =⇒ ∆ ⊢ [t1 /α]A2 ∗ B2
∆ ⊢ ∀(α ∗ A1 ). A2 ∗ B2147

10 Future WorkThe universal rule is very convenient as an elimination form: if we have a evidence of the
disjointness between a polymorphic type and another type, we can immediately obtain theknowledge that all suitable instantiations of the former are disjoint with the latter. However,the universal rule is very hard to use as an introduction rule: it requires us to inspect every
possible instantiation; it is getting even worse when we consider two polymorphic types. We
do not yet fully understand all the consequences of this rule. Another idea is perhaps weshould focus on the opposite side—i.e., what constitutes a non-disjointness relation. But thisidea seems more radical.

.. Declarative Typing
Putting disjointness aside, now we consider the typing rules. Most of the rules stay the same.
We remove rules FT-tabs and FT-tapp, since the syntax now does not include type abstractions and type applications. We add one rule:
FT-gen∆, α ∗ A; Γ ⊢ E ⇐ B ⇝ e∆; Γ ⊢ E ⇐ ∀(α ∗ A). B ⇝ Λα. e
Rule FT-gen says that E has type ∀(α∗A). B if E has type B in a context extended with α∗A.
Application becomes a little more complex:
FT-appI∆; Γ ⊢ E1 ⇒ A ⇝ e1

∆ ⊢ A ▷ A1 → A2∆; Γ ⊢ E2 ⇐ A1 ⇝ e2∆; Γ ⊢ E1 E2 ⇒ A2 ⇝ e1 e2The problem is that the inferred type A for E1 could be a polymorphic quantifier. We needto eliminate universals until we reach an arrow type. To achieve this, we use a matching
judgment ∆ ⊢ A ▷ A1 → A2 , which says that we can synthesize an arrow type A1 → A2
from A. Once we get an arrow type A1 → A2 , we use A1 to check against E2 . The matching
judgment [Siek et al. 2015b; Xie et al. 2018], first used in gradual type systems, is inductively
defined as follows:
m-forall∆ ⊢ t ∗ A1

∆ ⊢ [t/α]A1 ▷ B1 → B2

∆ ⊢ ∀(α ∗ A1 ). A2 ▷ B1 → B2

m-arr

∆ ⊢ A1 → A2 ▷ A1 → A2

Rule M-forall, as with rule IS-allL, works by guessing instantiations of polymorphic quantifiers with the requirement that the monotype t must meet the disjointness constraints.

148

10.3 Disjoint Polymorphism vs. Row Polymorphism
Rule M-arr is trivial, returning A1 → A2 as it is. An alternative to the matching judgment is the application judgment ∆ ⊢ A • e ⇒ C [Dunfield and Krishnaswami 2013], whichsays that if we apply a term of type A to an argument e, we get something of type C.
Of course the above is only a sketch; we have not studied the declarative system in full, nor
its metatheory. One potential problem is that now subtyping and disjointness are mutually
recursive (e.g., rule IS-allL uses disjointness and rule FD-tvarL uses subtyping), whichmight pose difficulty in terms of formalization. For coherence, we estimate that the proof
method described in this thesis should still work.

.. Algorithmic System
Having a declarative system is only a start. The major challenge is the corresponding algorithmic system. It is well-known that complete type inference is undecidable for intersection
types [Barendregt et al. 1983; Coppo et al. 1981]. Some restrictions are obviously in order,
leading to different points in the design space with varying degrees of expressiveness andtechnical difficulties. We are interested to see some research into the algorithmic system.

. Disjoint Polymorphism vs. Row Polymorphism
As we have alluded to in Section 9.4, it would be interesting to study the relationship between
disjoint polymorphism and row polymorphism, and in particular, whether the former subsumes the latter. As noted by Alpuim et al. [2017], disjoint polymorphism can already encode
polymorphic extensible records. For the sake of comparison, we pick the record calculus λ∥of Harper and Pierce [1991]—an explicitly-typed, second-order calculus that features singlefield records and a symmetric merge operator. In λ∥ , compatibility constraints are used tocapture negative information about fields. For example, r1 #r2 denotes the assertion thatthe record types r1 and r2 have disjoint sets of labels. To illustrate polymorphic extensible
records in λ∥ , Harper and Pierce show a function that takes two “disjoint” records x1 and x2 ,where x1 has at least a field l1 of type Int and x2 has at least a field l2 of type Int, and returns
the result of merging x1 and x2 (altering their syntax slightly):
Λα1 #({l1 : Int}, {l2 : Int}). Λα2 #(α1 , {l1 : Int}, {l2 : Int}).
λx1 : (α1 ∥{l1 : Int}). λx2 : (α2 ∥{l2 : Int}). x1 ∥x2
where r1 ∥r2 is the record type obtained by merging r1 and r2 , and is only defined if r1 #r2 .
The same operator is overloaded to merge two records on the term level. Central to theirsystem is the constrained quantification ∀α#R. t, where each record type variable is associ-149

10 Future Work
ated with a list of compatibility assumptions R, whose elements are record types (including
record type variables). The constrained type abstraction Λα#R. e is used to create values of
constrained quantification.
In F+
i , we can use disjoint quantification to express their constrained qualification, intersection types to merge record types, and the merge operator to merge records. The function
mentioned above can be written in F+
i as follows:Λ(α1 ∗ {l1 : Int} & {l2 : Int}). Λ(α2 ∗ α1 & {l1 : Int} & {l2 : Int}).
λx1 : α1 & {l1 : Int}. λx2 : α2 & {l2 : Int}. x1 , , x2∥
However, the merge operator in F+
i is more general than its counterpart in λ —i.e., it works

on any expressions, not just records. Another important difference is that their compatibility
judgment r1 #r2 effectively implies that their records must have distinct fields, whereas F+i
accepts duplicate fields as long as their types are disjoint. On a related note, λ∥ is powerfulenough to express a polymorphic, conflict-free function that merges two records of staticallyunknown fields:
mergeRcd = Λα1 #Empty. Λα2 #α1 . λx1 : α1 . λx2 : α2 . x1 ∥x2
where Empty is the empty record type. Compare it to our “more expressive” mergeAnyfunction:mergeAny = Λ(α1 ∗ ⊤). Λ(α2 ∗ α1 ). λx1 : α1 . λx2 : α2 . x1 , , x2
that merges any two expressions of statically unknown types. Thus we conjecture that F+icompletely subsumes λ∥ .

. Recursive Types
One extension of particular importance for modeling object-oriented languages is recursive
types. A great deal of lessons have been learned about calculi with recursive types and subtyping (see Pierce [2002, chap. 20]). But previous work has been focused on type systems
with substantially simpler subtyping relations. For simplicity, we are interested in adding
iso-recursive types, where a recursive type µX. A and its one-step unfolding are transformed
back and forth by a pair of functions fold and unfold. The most common definition of iso-150

10.4 Recursive Types
recursive subtyping is the Amber rule, popularized by Cardelli’s Amber language [Cardelli
1985].
RS-amber

RS-varΣ, X <: Y ⊢ A <: B

(X <: Y ) ∈ ΣΣ ⊢ µX. A <: µY. B

Σ ⊢ X <: Y

Rule RS-amber says that to show µX. A is a subtype of µY. B under some set of assumptions
Σ, it suffices to show A <: B under the additional assumption X <: Y . Note that this rule,
unlike most rules we have seen involving binding constructs on both sides, such as rule Sforall in Fig. 4.5, requires that the bound variables X and Y be renamed to be distinct
before the rule is applied. Rule RS-var allows us to conclude X <: Y if the assumptions
assume it.
While adding the above two rules to our subtyping relation in Fig. 3.3 (and extending
the other rules so that they pass Σ through from premises to conclusion) effectively yields
a declarative subtyping relation with recursive types, it is not entirely straightforward as to
how they affect disjointness, and in particular, under what conditions are two recursive types
disjoint. An initial attempt shows that the amber rule and the disjointness rule for functions
are in conflict.
The problem. For the ease of discussion, we do not consider top types, polymorphic types,
or BCD subtyping; then a guiding principle of designing disjointness rules is the simple disjointness specification (Definition 1): two types are disjoint if and only if they share no common supertypes. Now consider two recursive types µX. X → Int and µY. Y → Int. It is
not hard to see that they have no common supertypes (because of contravariance of function
argument subtyping). According to Definition 1, they are disjoint. On the other hand, since
the disjointness relation is structural, we should inspect the disjointness relation between
X → Int and Y → Int under certain relation over X and Y we do not know yet. However,according to rule D-arr, two functions are disjoint if their range types are disjoint; thus
X → Int and Y → Int are not disjoint. So we have µX. X → Int and µY. Y → Int are notdisjoint: a contradiction!
Positivity to the rescue. It is not obvious how to change either rule RS-amber orrule D-arr without disrupting the whole system. A possible solution is to restrict wheretype variables can occur. Instead of having a general recursive type µX. A where X may occur anywhere in A, we require that X occurs positively in A. Specifically, X occurs positivelyin A1 → A2 , if (1) X does not occur in A1 , (2) and X occurs positively in A2 . In general, any

151

10 Future Workoccurrences of X within the domain of a function type are negative occurrences, whereas any
occurrences of X within the range of a function type are positive occurrences. For example,
the two recursive types in the last subsection are not positive. While positivity does limit
the expressiveness of types, most useful datatypes (e.g., natural numbers, lists, streams) are
positive. For us, the positivity restriction for recursive types does work with the disjointness
rule for function types.
With positive recursive types, here is the disjointness rule for recursive types:
D-rec

A∗B
µX. A ∗ µY. B
We also need a few more disjointness axioms:
Dax-intRec

Dax-rcdRec

Dax-arrRec

Dax-intRVar

Int ∗ax µX. B{l : A} ∗ax µX. B

A1 → A2 ∗ax µX. B

Int ∗ax X

Dax-rcdRVar

Dax-arrRVar

Dax-recRVar{l : A} ∗ax X

A1 → A2 ∗ax XµY. A ∗ax X

An important observation is that any two distinct type variables are not disjoint. A few examples: µX. Int → X and µY. Int → Y are not disjoint; µX. Int → X and µY. Bool →Y & Int are not disjoint; µX. Int → X and µY. Int → Int → Y are disjoint. Note that theabove is only a sketch; it remains to see whether the disjointness rules are equivalent to thespecification.
Another gnarly issue is coherence. To model recursive types, we need to turn to stepindexed logical relations [Ahmed 2006]. We foresee it would be a major technical challenge
to adjust our coherence proof and its Coq mechanization.

. Other Extensions
There are several important extensions that should also be considered.

.. Union Types
Union types—as intersections’ dual—are also widely used in languages such as Ceylon and
Flow. Union types introduce an interesting subtyping relation: a union type A|B is a common

15210.5 Other Extensions
supertype of A and B; or more precisely, it is a least upper bound of A and B, as exhibited by
the following subtyping rules.
union

A <: CB <: CA|B <: CunionL

unionRA <: A|B

B <: A|B

Dunfield [2014] has shown that unions can be elaborated into sums, and the merge operator
also supports union elimination with two computationally distinct branches. We think that
adapting his approach to our setting should not be difficult. An immediate issue is disjointness. Adding union types to our system without any restrictions would cause ambiguity,
again. For example, Int & Bool can reach to Int|Bool via two paths, as shown below, eachleading to semantically different translations.
Int
<:

<:

Int & Bool

Int|Bool

<:

<:

Bool
More thoughts are needed to come up with a coherent system with union types.

.. Nominal Typing
Many widely-used OO languages feature nominal type systems where type names play a crucial role. In previous chapters, we often define short names for long or complex compoundtypes to improve readability, e.g., in Section 7.2, we have seen:type Editor = {on_key : String → String,
do_cut : String,show_help : String};
type Version = {version : String};

Such definitions are purely cosmetic: the name Editor is just an abbreviation for the recordon the right-hand side, and the two are interchangeable in every context. By contrast, in OO

153

10 Future Work
languages such as Java, every compound type (class declaration or interface definition) has a
name, and subtyping is explicitly declared between type names.
To blend in with our powerful structural subtyping relation, we need to clearly decide
which types are based on nominal subtyping, which are based on structural subtyping andhow they interact. A rough idea, following the Moby type system [Fisher and Reppy 2000],is to separate class types from object types, as we did for trait types. Subtyping on class types
is nominal, while objects are compared structurally. This is just a high-level intuition; ofcourse there are other details (e.g., disjointness) that need to be worked out. A pleasant
property of nominal systems, and also related to our extension of recursive types, is that they
offer a natural account of recursive types: if we look at the amber rule RS-amber, an explicit
subtyping relation X <: Y is added to the context when two recursive types are compared.

.. Mutable State
Another direction for future work is to add mutable state, which would affect two aspects ofour metatheory: type safety and coherence. For type safety, we expect that lessons learned
from previous work on family polymorphism and mutability on OO languages apply to ourwork. For example, it is well-known that subtyping in the presence of mutable state often
needs restrictions. Given such suitable restrictions we expect that type-safety in the presenceof mutability still holds. For coherence, it would be a major technical challenge to adjust ourcoherence proof and its Coq mechanization: logical relations that account for mutable state
introduce significant complexity (e.g., see Ahmed [2004]).

154Part VI
Epilogue155

 Conclusion
In this thesis we have argued that the combination of disjoint intersection types, a powerfulsubtyping relation and parametric polymorphism greatly improve the state-of-art techniquefor modularity and code reuse. In the course of our investigation, we have gradually introduced three new typed calculi with increasing expressiveness:• The λ+i calculus is the basic calculus with disjoint intersection types and a powerfulsubtyping relation. We have shown that it captures the essence of nested composition,
enables a simple solution to the expression problem. In order to prove coherence, wehave proposed the canonicity relation based on logical relations.
+• The F+i calculus, building on λi , supports parametric polymorphism. We have shownthat it improves upon the finally tagless [Carette et al. 2009] and object algebra [Oliveira
and Cook 2012] approaches and support advanced compositional designs, and enables
the development of highly modular and reusable programs. We have also extended thecanonicity relation to establish coherence property of F+i .
• SEDEL—an object-oriented language design—building on F+
i , supports, among others, typed first-class traits. We have illustrated the applicability of SEDEL with several
example uses for first-class traits. Furthermore, we have conducted a case study thatmodularizes programming language interpreters. The case study demonstrates thatthe state-of-art encodings of extensible designs are greatly improved by SEDEL.
Of course there are several noteworthy limitations in our proposed calculi. (1) Lack ofmutable state, which is a desirable feature in modern programming languages, and also very
important in order for mainstream languages to adopt some of the proposed language mechanisms in this thesis. (2) Lack of recursive types, which is important for modeling objectoriented language. (3) Poor runtime performance due to redundant coercion applications(e.g., applying multiple ids). As we remarked earlier, our generated coercions are not efficient in terms of space. There is existing work on space-efficient coercions [Herman et al.
2010; Siek et al. 2015a], which we expect should be applicable to the work in this thesis.
Hopefully we have convinced the reader that disjoint intersection types have great potential to serve as a foundation for powerful, flexible and yet type-safe and easy to reason157

11 Conclusion
object-oriented languages. We hope that the concepts and the methods described in thisthesis may serve as a helpful guide to researchers and programmers alike in their attempts
to understand and build better software. Thus this thesis serves as a stepping stone for further investigation of disjoint intersection types in conjunction with other type disciplines. A
great number of open questions, new research directions lie ahead (cf. Chapter 10).

158

Bibliography[Citing pages are listed after each reference.]
Amal Ahmed. 2004. Semantics of types for mutable state. Ph.D. Dissertation. Princeton
University. [cited on page 154]
Amal Ahmed. 2006. Step-indexed syntactic logical relations for recursive and quantified
types. In European Symposium on Programming (ESOP). [cited on page 152]
Eric E. Allen, Jonathan Bannet, and Robert Cartwright. 2003. A first-class approach to genericity. In Object-Oriented Programming Systems, Languages and Applications (OOPSLA).
[cited on page 137]
João Alpuim, Bruno C. d. S. Oliveira, and Zhiyuan Shi. 2017. Disjoint Polymorphism. In
European Symposium on Programming (ESOP). [cited on pages 8, 9, 10, 22, 23, 35, 40, 42,
44, 66, 67, 132, 134, 135, and 149]
Nada Amin, Adriaan Moors, and Martin Odersky. 2012. Dependent object types. In Workshop on Foundations of Object-Oriented Languages. [cited on page 8]Davide Ancona, Giovanni Lagorio, and Elena Zucca. 2003. Jam–designing a Java extension
with mixins. In Transactions on Programming Languages and Systems (TOPLAS). [cited
on page 137]
Henk Barendregt, Mario Coppo, and Mariangiola Dezani-Ciancaglini. 1983. A filter lambda
model and the completeness of type assignment. The journal of symbolic logic 48, 04 (1983),
931–940. [cited on pages 9, 16, 35, 41, 66, 132, and 149]
Jan Bessai, Boris Düdder, Andrej Dudenhefner, Tzu-Chun Chen, and Ugo de’Liguoro. 2014.
Typing Classes and Mixins with Intersection Types. In Workshop on Intersection Types andRelated Systems (ITRS). [cited on page 132]
Lorenzo Bettini, Sara Capecchi, and Ferruccio Damiani. 2013a. On flexible dynamic trait
replacement for Java-like languages. Science of Computer Programming 78, 7 (2013), 907– 932. [cited on page 140]

159

Bibliography
Lorenzo Bettini and Ferruccio Damiani. 2017. Xtraitj: Traits for the Java platform. Journalof Systems and Software 131 (2017), 419 – 441. [cited on page 138]
Lorenzo Bettini, Ferruccio Damiani, Ina Schaefer, and Fabio Strocco. 2013b. TraitRecordJ:
A programming language with traits and records. Science of Computer Programming 78,
5 (2013), 521 – 541. [cited on page 138]
Xuan Bi and Bruno C. d. S. Oliveira. 2018. Typed First-Class Traits. In European Conference
on Object-Oriented Programming (ECOOP). [cited on page 13]
Xuan Bi, Bruno C. d. S. Oliveira, and Tom Schrijvers. 2018. The Essence of Nested Composition. In European Conference on Object-Oriented Programming (ECOOP). [cited on
pages 13, 35, 43, 82, 132, and 134]
Xuan Bi, Ningning Xie, Bruno C. d. S. Oliveira, and Tom Schrijvers. 2019. Distributive Disjoint Polymorphism for Compositional Programming. Technical Report. The Universityof Hong Kong. https://bixuanzju.github.io/files/ESOP2019.pdf [cited on
page 13]
Dariusz Biernacki and Piotr Polesiuk. 2015. Logical relations for coherence of effect subtyping. In International Conference on Typed Lambda Calculi and Applications (TLCA). [cited
on page 132]
Lasse Blaauwbroek. 2017. On the Interaction Between Unrestricted Union and Intersection
Types and Computational Effects. Master’s thesis. Technical University Eindhoven. [cited
on page 134]
Viviana Bono, Amit Patel, and Vitaly Shmatikov. 1999. A core calculus of classes and mixins.
In European Conference on Object-Oriented Programming (ECOOP). [cited on page 137]
Gilad Bracha. 1992. The programming language jigsaw: mixins, modularity and multiple inheritance. Ph.D. Dissertation. Dept. of Computer Science, University of Utah. [cited on
pages 5, 6, 96, and 137]
Gilad Bracha and William R. Cook. 1990. Mixin-based inheritance. In Object-Oriented Programming, Systems, Languages and Applications (OOPSLA). [cited on pages 5, 6, 24, 96,
132, and 137]
Gilad Bracha and Gary Lindstrom. 1992. Modularity meets Inheritance. In International
Conference on Computer Languages. [cited on page 140]

160

Bibliography
Val Breazu-Tannen, Thierry Coquand, Carl A. Gunter, and Andre Scedrov. 1991. Inheritanceas implicit coercion. Information and Computation 93, 1 (1991), 172–221.

[cited on

page 131]
Richard P Brent and Hsiang-Tsung Kung. 1980. The chip complexity of binary arithmetic.
In Proceedings of the twelfth annual ACM symposium on Theory of computing. 190–200.
[cited on page 57]
Kim Bruce, Luca Cardelli, Giuseppe Castagna, Gary T Leavens, Benjamin Pierce, et al. 1996.
On binary methods. In Theory and Practice of Object Systems. [cited on page 35]
Kim B Bruce, Martin Odersky, and Philip Wadler. 1998. A statically safe alternative to virtual
types. In European Conference on Object-Oriented Programming. [cited on page 138]
Kim B. Bruce, Angela Schuett, and Robert van Gent. 1995. PolyTOIL: A Type-Safe Polymorphic Object-Oriented Language. In European Conference on Object-Oriented Programming(ECOOP). [cited on page 139]
Luca Cardelli. 1985. Amber. In Combinators and Functional Programming Languages, Thirteenth Spring School of the LITP. [cited on page 151]
Luca Cardelli and Peter Wegner. 1985. On understanding types, data abstraction, and polymorphism. Comput. Surveys 17, 4 (1985), 471–523.

[cited on pages 10, 71, 74, 131,

and 135]
Jacques Carette, Oleg Kiselyov, and Chung-Chieh Shan. 2009. Finally tagless, partially evaluated: Tagless staged interpreters for simpler typed languages. Journal of Functional Programming 19, 05 (2009), 509. [cited on pages 10, 55, 56, 128, and 157]
Giuseppe Castagna, Giorgio Ghelli, and Giuseppe Longo. 1992. A calculus for overloadedfunctions with subtyping. In Conference on LISP and Functional Programming. [cited on
pages 8, 17, and 134]
Giuseppe Castagna and Victor Lanvin. 2017. Gradual typing with union and intersection
types. Proceedings of the ACM on Programming Languages 1, ICFP (aug 2017), 1–28. [cited
on page 135]
Giuseppe Castagna, Kim Nguyen, Zhiwu Xu, Hyeonseung Im, Sergueï Lenglet, and Luca
Padovani. 2014. Polymorphic functions with set-theoretic types: part 1: syntax, semantics,
and evaluation. In Principles of Programming Languages (POPL). [cited on page 135]

161

Bibliography
David Clarke, Sophia Drossopoulou, James Noble, and Tobias Wrigstad. 2007. Tribe: More
Types for Virtual Classes. In International Conference on Aspect-Oriented Software Development (AOSD). [cited on pages 7 and 138]
Adriana B Compagnoni and Benjamin C Pierce. 1996. Higher-order intersection types andmultiple inheritance. Mathematical Structures in Computer Science (MSCS) 6, 5 (1996),
469–501. [cited on pages 8 and 15]
Steve Cook. 1987. Varieties of inheritance. In Addendum to the Proceedings on Objectoriented Programming Systems, Languages and Applications (Addendum). 35–40. [cited
on page 23]
William R. Cook. 1989. A Denotational Semantics of Inheritance. Ph.D. Dissertation. Brown
University. [cited on pages 11, 111, and 118]
William R. Cook. 1990. Object-oriented programming versus abstract data types. In Workshop/School/Symposium of the REX Project (Research and Education in Concurrent Systems). [cited on page 121]
William R. Cook. 2013. Anatomy of Programming Languages. http://www.cs.utexas.
edu/~wcook/anatomy/ [cited on pages 12, 121, and 125]
William R. Cook, Walter Hill, and Peter S Canning. 1989. Inheritance is not subtyping. In
Principles of Programming Languages (POPL). [cited on page 16]
William R. Cook and Jens Palsberg. 1989. A denotational semantics of inheritance and its
correctness. In Object-Oriented Programming: Systems, Languages and Applications (OOPSLA). [cited on pages 11, 28, 111, and 117]
Mario Coppo and Mariangiola Dezani-Ciancaglini. 1978. A new type assignment for λterms. 19 (01 1978), 139–156. [cited on pages 8 and 15]
Mario Coppo, Mariangiola Dezani-Ciancaglini, and Betti Venneri. 1981. Functional Characters of Solvable Terms. Mathematical Logic Quarterly 27, 2-6 (1981), 45–58. [cited on
pages 108 and 149]
Andrea Corradi, Marco Servetto, and Elena Zucca. 2012. DeepFJig — Modular compositionof nested classes. The Journal of Object Technology 11, 2 (2012), 1:1. [cited on pages 7
and 137]
Karl Crary, Robert Harper, and Sidd Puri. 1999. What is a recursive module?. In Programming Language Design and Implementation (PLDI). [cited on page 140]

162

Bibliography
Pierre-Louis Curien and Giorgio Ghelli. 1992. Coherence of subsumption, minimum typing and type-checking in F≤ . Mathematical Structures in Computer Science (MSCS) 2, 01(1992), 55. [cited on page 131]
Haskell B. Curry and Robert Feys. 1958. Combinatory Logic. Vol. 1. North Holland. Second
edition, 1968. [cited on page 15]
Rowan Davies and Frank Pfenning. 2000. Intersection types and computational effects. In
International Conference on Functional Programming (ICFP). [cited on pages 8 and 20]
Stéphane Ducasse, Oscar Nierstrasz, Nathanael Schärli, Roel Wuyts, and Andrew P. Black.
2006. Traits: A Mechanism for Fine-grained Reuse. Transactions on Programming Languages and Systems (TOPLAS) 28, 2 (2006), 331–388. [cited on pages 6, 9, 96, 100, 107,
118, 137, and 140]
Joshua Dunfield. 2014. Elaborating intersection and union types. Journal of Functional Programming (JFP) 24, 2-3 (2014), 133–165. [cited on pages 8, 17, 18, 19, 35, 134, and 153]
Joshua Dunfield and Neelakantan R Krishnaswami. 2013. Complete and Easy Bidirectional
Typechecking for Higher-Rank Polymorphism. In International Conference on Functional
Programming (ICFP). [cited on pages 146, 147, and 149]
Joshua Dunfield and Frank Pfenning. 2003. Type assignment for intersections and unions
in call-by-value languages. In Foundations of Software Science and Computation Structure(FoSSaCS). [cited on page 8]
ECMA International. 2015. ECMAScript 2015 Language Specification (6th ed.). Geneva.
http://www.ecma-international.org/ecma-262/6.0/ECMA-262.pdf [cited on
page 97]
Erik Ernst. 2000. gbeta-a language with virtual attributes, Block Structure, and Propagating,
Dynamic Inheritance. DAIMI Report Series 29, 549 (2000). [cited on page 26]
Erik Ernst. 2001. Family Polymorphism. In European Conference on Object-Oriented Programming (ECOOP). [cited on pages 5, 7, 25, 36, and 138]
Erik Ernst. 2003. Higher-Order Hierarchies. In European Conference on Object-Oriented
Programming. [cited on page 38]
Erik Ernst. 2004. The expression problem, Scandinavian style. On Mechanisms For Specialization (2004), 27. [cited on pages 15, 25, 26, 27, and 35]

163

Bibliography
Erik Ernst, Klaus Ostermann, and William R. Cook. 2006. A Virtual Class Calculus. In
Symposium on Principles of Programming Languages (POPL). [cited on pages 7 and 138]
Facebook. 2014. Flow. https://flow.org/. [cited on page 8]
Robert Bruce Findler, John Clements, Cormac Flanagan, Matthew Flatt, Shriram Krishnamurthi, Paul Steckler, and Matthias Felleisen. 2002. DrScheme: a programming environment for Scheme. Journal of Functional Programing 12, 2 (2002), 159–182.

[cited on

page 136]
Kathleen Fisher and John Reppy. 2000. Extending Moby with inheritance-based subtyping.
In European Conference on Object-Oriented Programming. 83–107. [cited on page 154]
Kathleen Fisher and John Reppy. 2004. A typed calculus of traits. In Workshop on Foundationsof Object-Oriented Languages. [cited on pages 106 and 137]
Matthew Flatt, Robert Bruce Findler, and Matthias Felleisen. 2006. Scheme with Classes,
Mixins, and Traits. In Programming Languages and Systems (APLAS). [cited on pages 95,
136, and 137]
Matthew Flatt, Shriram Krishnamurthi, and Matthias Felleisen. 1998. Classes and mixins. In
Principles of Programming Languages (POPL). [cited on page 137]
Maarten M Fokkinga. 1989. Tupling and mutumorphisms. Squiggolist 1, 4 (1989). [cited on
page 58]
Tim Freeman and Frank Pfenning. 1991. Refinement types for ML. In Conference on Programming Language Design and Implementation (PLDI). [cited on page 8]
Benedict R Gaster and Mark P Jones. 1996. A polymorphic type system for extensible records
and variants. Technical Report. University of Nottingham. [cited on page 135]
Jeremy Gibbons and Nicolas Wu. 2014. Folding domain-specific languages: deep and shallow
embeddings (functional Pearl). In ICFP. ACM, 339–347. [cited on page 56]
Robert Harper. 2016. Practical Foundations for Programming Languages. Cambridge University Press. [cited on page 78]
Robert Harper and Benjamin Pierce. 1991. A Record Calculus Based on Symmetric Concatenation. In Principles of Programming Languages (POPL). [cited on pages 135 and 149]
Fritz Henglein. 1994. Dynamic typing: syntax and proof theory. Science of Computer Programming 22, 3 (6 1994), 197–230. [cited on pages 40 and 44]

164

Bibliography
David Herman, Aaron Tomb, and Cormac Flanagan. 2010. Space-efficient gradual typing.
Higher-Order and Symbolic Computation 23, 2 (2010), 167. [cited on pages 45 and 157]
Roger Hindley. 1969. The principal type-scheme of an object in combinatory logic. Transactions of the american mathematical society 146 (1969), 29–60. [cited on page 89]
Ralf Hinze. 2004. An algebra of scans. In International Conference on Mathematics of Program
Construction. 186–210. [cited on page 59]
Paul Jolly, Sophia Drossopoulou, Christopher Anderson, and Klaus Ostermann. 2004. Simple dependent types: Concord. In European Conference on Object-Oriented Programming
Workshop on Formal Techniques for Java Programs (FTfJP). [cited on page 139]
Timothy Jones, Michael Homer, James Noble, and Kim B. Bruce. 2016. Object InheritanceWithout Classes. In European Conference on Object-Oriented Programming (ECOOP).
[cited on page 139]
Shriram Krishnamurthi, Matthias Felleisen, and Daniel P Friedman. 1998. Synthesizing
object-oriented and functional design to promote re-use. In European Conference on
Object-Oriented Programming. [cited on page 121]
Toshihiko Kurata and Masako Takahashi. 1995. Decidable properties of intersection typesystems. Typed Lambda Calculi and Applications (1995), 297–311. [cited on page 132]
Giovanni Lagorio, Marco Servetto, and Elena Zucca. 2012. Featherweight Jigsaw —Replacing inheritance by composition in Java-like languages. Information and Computation 214(2012), 86 – 111. [cited on page 137]
Joachim Lambek. 1985. Cartesian closed categories and typed λ-calculi. In LITP Spring
School on Theoretical Computer Science. 136–175. [cited on page 141]
Olivier Laurent. 2012a. Intersection types with subtyping by means of cut elimination. Fundamenta Informaticae 121, 1-4 (2012), 203–226. [cited on pages 48 and 132]
Olivier Laurent. 2012b. A syntactic introduction to intersection types. (2012). Unpublishednote. [cited on pages 48 and 49]
Olivier Laurent. 2018. Intersection Subtyping with Constructors. In Proceedings of the Ninth
Workshop on Intersection Types and Related Systems. [cited on pages 48 and 133]
Joseph Lee, Jonathan Aldrich, Troy Shaw, and Alex Potanin. 2015. A Theory of Tagged
Objects. In European Conference on Object-Oriented Programming (ECOOP). [cited on
pages 6, 96, 107, and 136]

165

Bibliography
Ole Lehrmann Madsen, Birger Mø-Pedersen, and Kristen Nygaard. 1993. Object-oriented
Programming in the BETA Programming Language. Addison-Wesley. [cited on page 138]
Daan Leijen. 2005. Extensible records with scoped labels. Trends in Functional Programming
5 (2005), 297–312. [cited on page 135]
Roberto E Lopez-Herrejon, Don Batory, and William Cook. 2005. Evaluating support forfeatures in advanced modularization technologies. In European Conference on ObjectOriented Programming (ECOOP). [cited on page 125]David MacQueen. 1984. Modules for standard ML. In Symposium on LISP and functional
programming (LFP). [cited on pages 4 and 140]
O. L. Madsen and B. Moller-Pedersen. 1989. Virtual classes: a powerful mechanism in objectoriented programming. In Object-Oriented Programming, Systems, Languages, and Applications (OOPLAS). [cited on page 138]
Bertrand Meyer. 1987. Eiffel: programming for reusability and extendibility. ACM Sigplan
Notices 22, 2 (1987), 85–94. [cited on page 139]
Microsoft. 2012. TypeScript. https://www.typescriptlang.org/. [cited on page 8]
Robin Milner. 1978. A theory of type polymorphism in programming. Journal of computerand system sciences 17, 3 (1978), 348–375. [cited on page 89]
Eugenio Moggi. 1991. Notions of computation and monads. Information and Computation
93, 1 (1991), 55–92. [cited on page 131]
J. Garrett Morris and James McKinna. 2019. Abstracting Extensible Data Types. In Principles
of Programming Languages (POPL). [cited on page 135]
James Hiram Morris Jr. 1969. Lambda-calculus models of programming languages. Ph.D.
Dissertation. Massachusetts Institute of Technology. [cited on page 78]
Fabian Muehlboeck and Ross Tate. 2018. Empowering union and intersection types withintegrated subtyping. In OOPSLA. [cited on page 133]
Peter Naur and Brian Randell (Eds.). 1969. Software Engineering: Report of a ConferenceSponsored by the NATO Science Committee, Garmisch, Germany, 7-11 Oct. 1968, Brussels,
Scientific Affairs Division, NATO. [cited on page 3]
Oscar Nierstrasz, Stéphane Ducasse, and Nathanael Schärli. 2006. Flattening Traits. Journalof Object Technology 5, 4 (2006), 129–148. [cited on pages 118 and 137]

166

Bibliography
James Noble, Andrew P. Black, Kim B. Bruce, Michael Homer, and Timothy Jones. 2017.
Grace’s Inheritance. Journal of Object Technology 16, 2 (2017), 2:1–35. [cited on page 139]Nathaniel Nystrom, Stephen Chong, and Andrew C. Myers. 2004. Scalable extensibility via
nested inheritance. In Object-Oriented Programming, Systems, Languages, and Applications(OOPSLA). [cited on pages 7, 25, and 139]Nathaniel Nystrom, Xin Qi, and Andrew C. Myers. 2006. J&: Nested Intersection for Scalable
Software Composition. In Object-Oriented Programming, Systems, Languages, and Applications (OOPSLA). [cited on pages 4 and 139]
Martin Odersky, Philippe Altherr, Vincent Cremet, Burak Emir, Sebastian Maneth, Stéphane
Micheloud, Nikolay Mihaylov, Michel Schinz, Erik Stenman, and Matthias Zenger. 2004.
An overview of the Scala programming language. Technical Report. EPFL. [cited on pages 8
and 102]
Martin Odersky and Konstantin Läufer. 1996. Putting Type Annotations to Work. In Symposium on Principles of Programming Languages (POPL). [cited on page 146]
Martin Odersky and Matthias Zenger. 2005. Scalable component abstractions. In ObjectOriented Programming: Systems, Languages and Applications (OOPSLA). [cited on pages 7
and 106]
Bruno C. d. S. Oliveira. 2009. Modular Visitor Components: A Practical Solution to theExpression Families Problem. In European Conference on Object Oriented Programming(ECOOP). [cited on pages 4, 11, 26, 121, 122, and 123]
Bruno C. d. S. Oliveira and William R. Cook. 2012. Extensibility for the Masses. In European
Conference on Object-Oriented Programming (ECOOP). [cited on pages 4, 10, 12, 26, 55,
121, 122, 124, and 157]
Bruno C. d. S. Oliveira, Ralf Hinze, and Andres Löh. 2006. Extensible and modular generics
for the masses. Trends in Functional Programming 7 (2006), 199–216. [cited on page 59]
Bruno C. d. S. Oliveira, Zhiyuan Shi, and João Alpuim. 2016. Disjoint intersection types. In
International Conference on Functional Programming (ICFP). [cited on pages 8, 9, 10, 15,
19, 20, 22, 35, 40, 42, 44, 48, 66, and 134]
Bruno C. d. S. Oliveira, Tijs Van Der Storm, Alex Loh, and William R Cook. 2013. FeatureOriented programming with object algebras. In European Conference on Object-Oriented
Programming (ECOOP). [cited on pages 7, 121, and 124]

167

Bibliography
Bruno C. d. S. Oliveira, Meng Wang, and Jeremy Gibbons. 2008. The visitor pattern as areusable, generic, type-safe component. In Object Oriented Programming: Systems, Languages and Applications (OOPSLA). [cited on page 122]
Simon Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, and Mark Shields. 2007. Practical Type Inference for Arbitrary-Rank Types. Journal of Functional Programming (JFP)
17, 1 (2007), 1–82. [cited on page 146]
Benjamin C Pierce. 1989. A decision procedure for the subtype relation on intersection types
with bounded variables. Technical Report. Carnegie Mellon University. [cited on pages 10,
12, 48, 49, 50, 52, 53, 132, and 133]
Benjamin C Pierce. 1991. Programming with intersection types and bounded polymorphism.
Ph.D. Dissertation. University of Pennsylvania. [cited on pages 55, 135, 136, and 143]
Benjamin C Pierce. 1994. Bounded quantification is undecidable. Information and Computation 112, 1 (1994), 131–165. [cited on pages 55, 71, 74, and 136]
Benjamin C. Pierce. 2002. Types and programming languages. MIT Press. [cited on pages 13
and 150]
Gordon Plotkin. 1973. Lambda-definability and logical relations. Edinburgh University.
[cited on pages 31, 78, and 81]
Garrel Pottinger. 1980. A type assignment for the strongly normalizable λ-terms. To HB
Curry: essays on combinatory logic, lambda calculus and formalism (1980), 561–577. [cited
on pages 8, 15, and 108]Xin Qi and Andrew C. Myers. 2009. Sharing Classes Between Families. In Conference on
Programming Language Design and Implementation (PLDI). [cited on page 139]
Redhat. 2011. Ceylon. https://ceylon-lang.org/. [cited on page 8]
Jakob Rehof and Paweł Urzyczyn. 2011. Finite combinatory logic with intersection types. In
International Conference on Typed Lambda Calculi and Applications. [cited on page 132]
Tillmann Rendel, Jonathan Immanuel Brachthäuser, and Klaus Ostermann. 2014. From Object Algebras to Attribute Grammars. In Object-Oriented Programming, Systems Languages
and Applications (OOPSLA). [cited on pages 121 and 124]
John C. Reynolds. 1983. Types, Abstraction and Parametric Polymorphism. In Proceedingsof the IFIP 9th World Computer Congress. [cited on pages 88 and 90]168

Bibliography
John C Reynolds. 1988. Preliminary design of the programming language Forsythe. Technical
Report. Carnegie Mellon University. [cited on pages 8, 16, 17, 19, 134, and 141]
John C. Reynolds. 1991. The coherence of languages with intersection types. In Lecture Notes
in Computer Science (LNCS). Springer Berlin Heidelberg, 675–700. [cited on pages 8, 18,131, 141, and 142]
John C Reynolds. 1997. Design of the programming language Forsythe. In ALGOL-likelanguages. 173–233. [cited on pages 17, 18, and 133]
Andreas Rossberg. 2015. 1ML – core and modules united (F-ing first-class modules). In
International Conference on Functional Programming (ICFP). [cited on page 140]
Andreas Rossberg and Derek Dreyer. 2013. Mixin’ Up the ML Module System. Transactions
on Programming Languages and Systems (TOPLAS) 35, 1 (2013), 1–84. [cited on page 140]
Andreas Rossberg, Claudio Russo, and Derek Dreyer. 2014. F-ing modules. Journal of Functional Programming (JFP) 24, 05 (2014), 529–607. [cited on page 140]
Claudio V. Russo. 2001. Recursive structures for standard ML. In International Conference
on Functional Programming (ICFP). [cited on page 140]
Chieri Saito and Atsushi Igarashi. 2009. Matching ThisType to subtyping. In Symposium on
Applied Computing (SAC). [cited on page 139]
Chieri Saito, Atsushi Igarashi, and Mirko Viroli. 2007. Lightweight family polymorphism.
Journal of Functional Programming (JFP) 18, 03 (2007). [cited on pages 7 and 139]Nathanael Schärli, Stéphane Ducasse, Oscar Nierstrasz, and Andrew P Black. 2003. Traits:
Composable units of behaviour. In European Conference on Object-Oriented Programming(ECOOP). [cited on pages 5, 6, 9, 24, 25, 96, 100, 105, 106, 107, 118, and 137]
Nathanael Scharli, St Ducasse, Roel Wuyts, Andrew Black, et al. 2003. Traits: The formal
model. Technical Report. Oregon Health & Science University. [cited on page 137]
Jan Schwinghammer. 2008. Coherence of subsumption for monadic types. Journal of Functional Programming (JFP) 19, 02 (2008), 157. [cited on page 131]
Marco Servetto and Elena Zucca. 2014. A meta-circular language for active libraries. Scienceof Computer Programming 95 (2014), 219 – 253. [cited on page 140]

169

Bibliography
Jeremy Siek, Peter Thiemann, and Philip Wadler. 2015a. Blame and coercion: together again
for the first time. In Conference on Programming Language Design and Implementation(PLDI). [cited on pages 45 and 157]
Jeremy G. Siek, Michael M Vitousek, Matteo Cimini, and John Tang Boyland. 2015b. Refined
Criteria for Gradual Typing. In LIPIcs-Leibniz International Proceedings in Informatics.
[cited on page 148]
Yannis Smaragdakis and Don S. Batory. 2000. Mixin-Based Programming in C++. In Generative and Component-Based Software Engineering (GCSE). [cited on page 99]
Charles Smith and Sophia Drossopoulou. 2005. Chai: Traits for Java-Like Languages. In
European Conference on Object-Oriented Programming (ECOOP).

[cited on pages 137

and 140]
Richard Statman. 1985. Logical relations and the typed λ-calculus. Information and Control
65, 2-3 (1985), 85–97. [cited on pages 31, 78, and 81]
Rick Statman. 2015. A Finite Model Property for Intersection Types. Electronic Proceedingsin Theoretical Computer Science 177 (2015), 1–9. [cited on page 132]
Wouter Swierstra. 2008. Data types à la carte. Journal of Functional Programming (JFP) 18,
4 (2008), 423–436. [cited on pages 4 and 26]
W. W. Tait. 1967. Intensional Interpretations of Functionals of Finite Type I. The Journal ofsymbolic logic 32, 2 (1967), 198–212. [cited on pages 31, 78, and 81]
Asumu Takikawa, T. Stephen Strickland, Christos Dimoulas, Sam Tobin-Hochstadt, andMatthias Felleisen. 2012. Gradual typing for first-class classes. In Object-oriented Programming: Systems, Languages and Applications (OOPSLA). [cited on pages 5, 6, 96, 97,
and 136]
Mads Torgersen. 2004. The Expression Problem Revisited. In European Conference on ObjectOriented Programming (ECOOP). [cited on pages 11, 26, 121, and 122]David Ungar and Randall B Smith. 1988. SELF: the power of simplicity (object-oriented language). In Thirty-Third IEEE Computer Society International Conference, Digest of Papers.
[cited on page 139]
Philip Wadler. 1998. The expression problem. Java-genericity mailing list (1998). [cited on
pages 4, 7, 26, and 121]

170

Bibliography
Mitchell Wand. 1987. Complete type inference for simple objects. In Symposium on Logic in
Computer Science (LICS). [cited on page 135]
Mitchell Wand. 1994. Type inference for objects with instance variables and inheritance.
Theoretical aspects of object-oriented programming (1994), 97–120. [cited on page 104]
Yanlin Wang and Bruno C. d. S. Oliveira. 2016. The expression problem, trivially!. In Proceedings of the 15th International Conference on Modularity. [cited on pages 4, 26, and 138]
Ningning Xie, Xuan Bi, and Bruno C. d. S. Oliveira. 2018. Consistent Subtyping for All. In
European Symposium on Programming (ESOP). [cited on page 148]
Pamela Zave. 1999. Faq sheet on feature interaction. Link: http://www. research. att. com/~pamela/faq. html (1999). [cited on page 125]
Mathhias Zenger and Martin Odersky. 2005. Independently Extensible Solutions to the Expression Problem. In Foundations of Object-Oriented Languages.

[cited on pages 4, 7,

and 26]
Yizhou Zhang and Andrew C. Myers. 2017. Familia: unifying interfaces, type classes, andfamily polymorphism. In Object-Oriented Programming, Systems, Languages, and Applications (OOPSLA). [cited on page 140]

171

Part VII
Technical Appendix

173

A Circuit Embeddings{Parallel Prefix Circuits DSL
Finally Tagless Encoding
Supporting zygo- and mutumorphisms-}{-# LANGUAGE ConstraintKinds #-}{-# LANGUAGE DataKinds #-}{-# LANGUAGE FlexibleContexts #-}{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE GADTs #-}{-# LANGUAGE KindSignatures #-}{-# LANGUAGE MultiParamTypeClasses #-}{-# LANGUAGE RankNTypes #-}{-# LANGUAGE ScopedTypeVariables #-}{-# LANGUAGE TypeApplications #-}{-# LANGUAGE TypeOperators #-}{-# LANGUAGE UndecidableInstances #-}{- Generic Definitions for Records -}data Record :: [*] → * where
Nil :: Record '[]Cons :: a → Record as → Record (a ': as)class In a as where
project :: Record as → a
instance {-# OVERLAPPING #-} In a (a ': as) where175

A Circuit Embeddings
project (Cons x _) = x
instance {-# OVERLAPPING #-} In a as ⇒ In a (b ': as) where
project (Cons _ xs) = project xs
data All c :: [*] → * where
AllNil :: All c '[]AllCons :: c a ⇒ All c as → All c (a ': as){- Circuit DSL Infrastructure -}class Circuit0 c where
identity_ :: Int → cfan_:: Int → cclass Circuit0 c ⇒ Circuit1 d c where
beside_:: Record d → Record d → c
above_:: Record d → Record d → cstretch_ :: [Int] → Record d → cclass Circuit2 d1 d2 where
modality :: All (Circuit1 d1) d2
instance Circuit2 d1 '[] where
modality = AllNil
instance (Circuit1 d1 a, Circuit2 d1 as) ⇒ Circuit2 d1 (a ': as) where
modality = AllCons modality
type Circuit3 d = Circuit2 d didentity :: forall d. Circuit3 d ⇒ Int → Record didentity = identity' (modality @d @d)where
identity' :: All (Circuit1 d1) d2 → Int → Record d2
identity' AllNil
n = Nil
identity' (AllCons m) n = Cons (identity_ n) (identity' m n)
fan :: forall d. Circuit3 d ⇒ Int → Record dfan = fan' (modality @d @d)
where

176

fan' :: All (Circuit1 d1) d2 → Int → Record d2
fan' AllNil
n = Nilfan' (AllCons m) n = Cons (fan_ n) (fan' m n)
beside :: forall d. Circuit3 d ⇒ Record d → Record d → Record dbeside = beside' (modality @ d @ d)wherebeside' :: All (Circuit1 d1) d2 → Record d1 → Record d1 → Record d2
beside' AllNil
c1 c2 = Nilbeside' (AllCons m) c1 c2 = Cons (beside_ c1 c2) (beside' m c1 c2)
above :: forall d. Circuit3 d ⇒ Record d → Record d → Record dabove = above' (modality @ d @ d)
where
above' :: All (Circuit1 d1) d2 → Record d1 → Record d1 → Record d2
above' AllNil
c1 c2 = Nilabove' (AllCons m) c1 c2 = Cons (above_ c1 c2) (above' m c1 c2)
stretch :: forall d. Circuit3 d ⇒ [Int] → Record d → Record dstretch = stretch' (modality @ d @ d)wherestretch' :: All (Circuit1 d1) d2 → [Int] → Record d1 → Record d2
stretch' AllNil
ws c = Nilstretch' (AllCons m) ws c = Cons (stretch_ ws$ c) (stretch' m ws c){- Shallow Embeddings -}-- Width
newtype Width = Width { width :: Int }instance Circuit0 Width where
identity_ n= Width nfan_ n= Width ninstance In Width d ⇒ Circuit1 d Width where
beside_ c1 c2 = Width (width (project c1) + width (project c2))
above_ c1 c2= project c1
stretch_ ws c = Width (sum ws)-- Well-Sizedness177

A Circuit Embeddings

newtype WellSized = WellSized { wellSized :: Bool }instance Circuit0 WellSized where
identity_ n = WellSized True
fan_ n= WellSized True
instance (In WellSized d, In Width d) ⇒ Circuit1 d WellSized where
beside_ c1 c2 = WellSized (wellSized (project c1) && wellSized (projectc2))
above_ c1 c2 =WellSized(width (project c1) == width (project c2) &&wellSized (project c1) && wellSized (project c2))
stretch_ ws c =WellSized (wellSized (project c) && length ws == width (project c)){- Example -}test :: Record '[Width, WellSized]
test = above (identity 5) (fan 5)
test' =
case test ofCons x (Cons y Nil) → (width x, wellSized y)178

B Decidability
Definition 18 (Size of Q).
size([]) = 0
size(Q, l) = size(Q)size(Q, A) = size(Q) + size(A)size(Q, α ∗ A) = size(Q) + size(A)Definition 19 (Size of types).
size(c) = 1
size(A → B) = size(A) + size(B) + 1
size(A & B) = size(A) + size(B) + 1
size({l : A}) = size(A) + 1
size(∀(α ∗ A). B) = size(A) + size(B) + 1
Lemma 4.12 (Decidability of algorithmic subtyping). Given Q, A and B, it is decidable whether
there exists co, such that Q ⊢ A ≺: B ⇝ co.
Proof. Let the judgment Q ⊢ A ≺: B ⇝ co be measured by size(Q) + size(A) + size(B).
For each subtyping rule, we show that every inductive premise is smaller than the conclusion.
• Rules A-const, A-top, and A-bot have no premise.
• CaseA-and

Q ⊢ A ≺: B1 ⇝ co1

Q ⊢ A ≺: B2 ⇝ co2

Q ⊢ A ≺: B1 & B2 ⇝ JQK& ◦ ⟨co1 , co2 ⟩In both premises, they have the same Q and A, but B1 and B2 are smaller than B1 & B2 .

179

B Decidability• CaseA-arr

Q, B1 ⊢ A ≺: B2 ⇝ co
Q ⊢ A ≺: B1 → B2 ⇝ co
The size of premise is smaller than the conclusion as size(B1 → B2 ) = size(B1 ) +size(B2 ) + 1.
• CaseA-rcd

Q, l ⊢ A ≺: B ⇝ co
Q ⊢ A ≺: {l : B} ⇝ co
In premise, the size is size(Q, l)+size(A)+size(B) = size(Q)+size(A)+size(B),which is smaller than size(Q) + size(A) + size({l : B}) = size(Q) + size(A) +size(B) + 1.
• CaseA-forallQ, α ∗ B1 ⊢ A ≺: B2 ⇝ co
Q ⊢ A ≺: ∀(α ∗ B1 ). B2 ⇝ co
The size of premise is smaller than the conclusion as size(Q) + size(A) + size(∀(α ∗B1 ). B2 ) = size(Q) + size(A) + size(B1 ) + size(B2 ) + 1 > size(Q, α ∗ B1 ) +size(A) + size(B2 ) = size(Q) + size(B1 ) + size(A) + size(B2 ).
• CaseA-arrConst[] ⊢ A ≺: A1 ⇝ co1

Q ⊢ A2 ≺: c ⇝ co2

A, Q ⊢ A1 → A2 ≺: c ⇝ co1 → co2
In the first premise, the size is smaller than the conclusion because of the size of Q and
A2 . In the second premise, the size is smaller than the conclusion because size(A1 →
A2 ) > size(A2 ).
• CaseA-rcdConst

Q ⊢ A ≺: c ⇝ col, Q ⊢ {l : A} ≺: c ⇝ co
The size of premise is smaller as size(l, Q) + size({l : A}) + size(c) = size(Q) +size(A) + size(c) + 1 > size(Q) + size(A) + size(c).

180• CaseA-andConst

Q ⊢ A1 ≺: c ⇝ co
Q ⊢ A1 & A2 ≺: c ⇝ co ◦ π1
The size of premise is smaller as size(A1 & A2 ) = size(A1 )+size(A2 )+1 > size(A1 ).
• CaseA-andConst

Q ⊢ A2 ≺: c ⇝ co
Q ⊢ A1 & A2 ≺: c ⇝ co ◦ π2
The size of premise is smaller as size(A1 & A2 ) = size(A1 )+size(A2 )+1 > size(A2 ).
• CaseA-allConst[] ⊢ A ≺: A1

Q ⊢ A2 ≺: c ⇝ co(α ∗ A, Q) ⊢ ∀(α ∗ A1 ). A2 ≺: c ⇝ co∀
In the first premise, the size is smaller than the conclusion because of the size of Q and
A2 . In the second premise, the size is smaller than the conclusion because size(∀(β ∗A1 ). A2 ) > size(A2 ).

Lemma B.1 (Decidability of Top-like types). Given a type A, it is decidable whether ⌉A⌈.
Proof. Induction on the judgment ⌉A⌈. For each subtyping rule, we show that every inductive premise is smaller than the conclusion.
• rule TL-top has no premise.
• CaseTL-and⌉A⌈⌉B⌈⌉A & B⌈size(A & B) = size(A) + size(B) + 1, which is greater than size(A) and size(B).
• CaseTL-arr⌉B⌈⌉A → B⌈size(A → B) = size(A) + size(B) + 1, which is greater than size(B).

181

B Decidability• CaseTL-rcd⌉A⌈⌉{l : A}⌈size({l : A}) = size(A) + 1, which is greater than size(A).
• CaseTL-all⌉B⌈⌉∀(α ∗ A). B⌈
size(∀(α ∗ A). B) = size(A) + size(B) + 1, which is greater than size(B).

Lemma B.2 (Decidability of disjointness axioms checking). Given A and B, it is decidable
whether A ∗ax B.
Proof. A ∗ax B is decided by the shape of types, and thus it’s decidable.
Lemma 4.13 (Decidability of disjointness checking). Given ∆, A and B, it is decidable whether∆ ⊢ A ∗ B.
Proof. Let the judgment ∆ ⊢ A ∗ B be measured by size(A) + size(B). For each subtyping
rule, we show that every inductive premise is smaller than the conclusion.
• CaseFD-topL⌉A⌈∆⊢A∗B
By Lemma B.1, we know ⌉A⌈ is decidable.
• CaseFD-topR⌉B⌈∆⊢A∗B
By Lemma B.1, we know ⌉B⌈ is decidable.
• CaseFD-arr∆ ⊢ A2 ∗ B 2∆ ⊢ A1 → A2 ∗ B1 → B2size(A1 → A2 ) + size(B1 → B2 ) > size(A2 ) + size(B2 ).

182

• CaseFD-andL∆ ⊢ A1 ∗ B∆ ⊢ A2 ∗ B∆ ⊢ A1 & A2 ∗ B
size(A1 & A2 ) + size(B) is greater than size(A1 ) + size(B) and size(A2 ) + size(B).
• CaseFD-andR∆ ⊢ A ∗ B1

∆ ⊢ A ∗ B2

∆ ⊢ A ∗ B1 & B2size(B1 & B2 ) + size(A) is greater than size(B1 ) + size(A) and size(B2 ) + size(A).
• CaseFD-rcdEq∆⊢A∗B∆ ⊢ {l : A} ∗ {l : B}size({l : A}) + size({l : B}) is greater than size(A) + size(B).
• CaseFD-rcdNeq

l1 ̸= l2
∆ ⊢ {l1 : A} ∗ {l2 : B}
It’s decidable whether l1 is equal to l2 .
• CaseFD-tvarL(α ∗ A) ∈ ∆A <: B∆⊢α∗B
By Lemma 4.12, it’s decidable whether A <: B.
• CaseFD-tvarR(α ∗ A) ∈ ∆A <: B∆⊢B∗α
By Lemma 4.12, it’s decidable whether A <: B.
• CaseFD-forall∆, α ∗ A1 & A2 ⊢ B1 ∗ B2
∆ ⊢ ∀(α ∗ A1 ). B1 ∗ ∀(α ∗ A2 ). B2

183

B Decidabilitysize(∀(α ∗ A1 ). B1 ) + size(∀(α ∗ A2 ). B2 ) is greater than size(B1 ) + size(B2 ).
• CaseFD-ax

A ∗ax B∆⊢A∗B
By Lemma B.2 it is decidable.

184

C Proofs about SEDEL
Lemma 7.1. If ∆ ⊢ A then |∆| ⊢ |A|.
Proof. By simple induction on the derivation of the judgment.
Lemma 7.2. If A <: B then |A| <: |B|.
Proof. Most of them are straightforward. We only show rule TS-trait.
•

TS-trait

B1 <: A1A2 <: B2

Trait [A1 , A2 ] <: Trait [B1 , B2 ]|B1 | <: |A1 |By i.h.

|A2 | <: |B2 |By i.h.

|A1 | → |A2 | <: |B1 | → |B2 |By rule S-arr

Lemma C.1. If A ∗ax B then |A| ∗ax |B|.
Proof. Note that |Trait [A, B]| = |A| → |B|, the rest are immediate.
Lemma 7.3. If ∆ ⊢ A ∗ B then |∆| ⊢ |A| ∗ |B|.
Proof. By induction on the derivation of the judgment.
• Rules SD-topL, SD-topR, and SD-rcdNeq are immediate.
•SD-tvarL(α ∗ A) ∈ ∆A <: B∆⊢α∗B

185

C Proofs about SEDEL|A| <: |B|

By Lemma 7.2(α ∗ A) ∈ ∆Given(α ∗ |A|) ∈ |∆|Above|∆| ⊢ α ∗ |B|

By rule FD-tvarL•

SD-tvarR(α ∗ A) ∈ ∆A <: B∆⊢B∗α|A| <: |B|

By Lemma 7.2(α ∗ A) ∈ ∆Given(α ∗ |A|) ∈ |∆|Above|∆| ⊢ |B| ∗ α

By rule FD-tvarR•SD-forall∆, α ∗ A1 & A2 ⊢ B1 ∗ B2
∆ ⊢ ∀(α ∗ A1 ). B1 ∗ ∀(α ∗ A2 ). B2|∆|, α ∗ |A1 | & |A2 | ⊢ |B1 | ∗ |B2 |By i.h.

|∆| ⊢ ∀(α ∗ |A1 |). |B1 | ∗ ∀(α ∗ |A2 |). |B2 |By rule FD-forall•

SD-rcdEq∆⊢A∗B∆ ⊢ {l : A} ∗ {l : B}|∆| ⊢ |A| ∗ |B|

By i.h.

|∆| ⊢ {l : |A|} ∗ {l : |B|}

By rule FD-rcdEq•

SD-arr∆ ⊢ A 2 ∗ B2
∆ ⊢ A 1 → A 2 ∗ B1 → B 2|∆| ⊢ |A2 | ∗ |B2 |By i.h.

|∆| ⊢ |A1 | → |A2 | ∗ |B1 | → |B2 |By rule FD-arr

186

•

SD-andL∆ ⊢ A1 ∗ B∆ ⊢ A2 ∗ B∆ ⊢ A 1 & A2 ∗ B|∆| ⊢ |A1 | ∗ |B|

By i.h.

|∆| ⊢ |A2 | ∗ |B|

By i.h.

|∆| ⊢ |A1 | & |A2 | ∗ |B|

By rule FD-andL•

SD-andR∆ ⊢ A ∗ B1

∆ ⊢ A ∗ B2

∆ ⊢ A ∗ B1 & B2|∆| ⊢ |A| ∗ |B1 |By i.h.

|∆| ⊢ |A| ∗ |B2 |By i.h.

|∆| ⊢ |A| ∗ |B1 | & |B2 |By rule FD-andR•

SD-trait∆ ⊢ A 2 ∗ B2
∆ ⊢ Trait [A1 , A2 ] ∗ Trait [B1 , B2 ]|∆| ⊢ |A2 | ∗ |B2 |By i.h.

|∆| ⊢ |A1 | → |A2 | ∗ |B1 | → |B2 |By rule FD-arr•

SD-traitArr

∆ ⊢ A 2 ∗ B2
∆ ⊢ Trait [A1 , A2 ] ∗ B1 → B2|∆| ⊢ |A2 | ∗ |B2 |By i.h.

|∆| ⊢ |A1 | → |A2 | ∗ |B1 | → |B2 |By rule FD-arr•

SD-traitArr

∆ ⊢ A 2 ∗ B2
∆ ⊢ A1 → A2 ∗ Trait [B1 , B2 ]187

C Proofs about SEDEL|∆| ⊢ |A2 | ∗ |B2 |By i.h.

|∆| ⊢ |A1 | → |A2 | ∗ |B1 | → |B2 |By rule FD-arr

•

SD-ax

A ∗ax B∆⊢A∗B
|A| ∗ax |B|

By Lemma C.1|∆| ⊢ |A| ∗ |B|

By rule FD-ax

Theorem 7.4 (Type-safe translation). We have that:• If ∆; Γ ⊢ T ⇒ A ⇝ E then |∆|; |Γ| ⊢ E ⇒ |A|.
• If ∆; Γ ⊢ T ⇐ A ⇝ E then |∆|; |Γ| ⊢ E ⇐ |A|.
Proof. By induction on the typing judgment.
• Rules ST-top, ST-int, and ST-var are immediate.
•

ST-anno∆; Γ ⊢ T ⇐ A ⇝ E∆; Γ ⊢ T : A ⇒ A ⇝ E : |A||∆|; |Γ| ⊢ E ⇐ |A|

By i.h.

|∆|; |Γ| ⊢ E : |A| ⇒ |A|

By rule FT-anno•

ST-app∆; Γ ⊢ T1 ⇒ A1 → A2 ⇝ E1∆; Γ ⊢ T2 ⇐ A1 ⇝ E2∆; Γ ⊢ T1 T2 ⇒ A2 ⇝ E1 E2|∆|; |Γ| ⊢ E1 ⇒ |A1 | → |A2 |By i.h.

|∆|; |Γ| ⊢ E2 ⇐ |A1 |By i.h.

|∆|; |Γ| ⊢ E1 E2 ⇒ |A2 |By rule FT-app

188

•

ST-tapp∆; Γ ⊢ T ⇒ ∀(α ∗ B1 ). B2 ⇝ E∆⊢A∆ ⊢ A ∗ B1∆; Γ ⊢ T A ⇒ [A/α]B2 ⇝ E |A||∆|; |Γ| ⊢ E ⇒ ∀(α ∗ |B1 |). |B2 |By i.h.

|∆| ⊢ |A|

By Lemma 7.1|∆| ⊢ |A| ∗ |B1 |By Lemma 7.3|∆|; |Γ| ⊢ E |A| ⇒ [|A|/α]|B2 |By rule FT-tapp•

ST-merge∆; Γ ⊢ T1 ⇒ A ⇝ E1∆; Γ ⊢ T2 ⇒ B ⇝ E2∆⊢A∗B∆; Γ ⊢ T1 , , T2 ⇒ A & B ⇝ E1 , , E2|∆|; |Γ| ⊢ E1 ⇒ |A|

By i.h.

|∆|; |Γ| ⊢ E2 ⇒ |B|

By i.h.

|∆| ⊢ |A| ∗ |B|

By Lemma 7.3|∆|; |Γ| ⊢ E1 , , E2 ⇒ |A| & |B|

By rule FT-merge•

ST-rcd∆; Γ ⊢ T ⇒ A ⇝ E∆; Γ ⊢ {l = T } ⇒ {l : A} ⇝ {l = E}|∆|; |Γ| ⊢ E ⇒ |A|

By i.h.

|∆|; |Γ| ⊢ {l = E} ⇒ {l : |A|}

By rule FT-rcd•

ST-proj∆; Γ ⊢ T ⇒ {l : A} ⇝ E∆; Γ ⊢ T .l ⇒ A ⇝ E.l|∆|; |Γ| ⊢ E ⇒ {l : |A|}

By i.h.

|∆|; |Γ| ⊢ E.l ⇒ |A|

By rule FT-proj

189

C Proofs about SEDEL•

ST-tabs∆⊢A∆, α ∗ A; Γ ⊢ T ⇒ B ⇝ E∆; Γ ⊢ Λ(α ∗ A). T ⇒ ∀(α ∗ A). B ⇝ Λ(α ∗ |A|). E|∆| ⊢ |A|

By Lemma 7.1|∆|, α ∗ |A|; |Γ| ⊢ E ⇒ |B|

By i.h.

|∆|; |Γ| ⊢ Λ(α ∗ |A|). E ⇒ ∀(α ∗ |A|). |B|

By rule FT-tabs•

ST-letrec∆; Γ, x : A ⊢ T1 ⇐ A ⇝ E1∆; Γ, x : A ⊢ T2 ⇒ B ⇝ E2∆; Γ ⊢ letrec x : A = T1 in T2 ⇒ B ⇝ letrec x : |A| = E1 in E2|∆|; |Γ|, x : |A| ⊢ E1 ⇐ |A|

By i.h.

|∆|; |Γ|, x : |A| ⊢ E2 ⇒ |B|

By i.h.

|∆|; |Γ| ⊢ letrec x : |A| = E1 in E2 ⇒ |B|•
ST-new∆; Γ ⊢ Ti ⇒ Trait [Ai , Bi ] ⇝ Ei
A <: Ai

i∈1..n∆; Γ ⊢ new [A]( Tii∈1..n∆ ⊢ B1 ∗ .. ∗ Bni∈1..nB1 & .. & Bn <: A

) ⇒ A ⇝ letrec self : |A| = (Ei self)i∈1..nin self|∆|; |Γ| ⊢ Ei ⇒ |Ai | → |Bi |By i.h.

|A| <: |Ai |By Lemma 7.2|∆| ⊢ |B1 | ∗ .. ∗ |Bn |By Lemma 7.3|B1 | & .. & |Bn | <: |A|

By Lemma 7.2|∆|; |Γ|, self : |A| ⊢ self ⇒ |A|

By rule FT-var|∆|; |Γ|, self : |A| ⊢ self ⇐ |Ai |By rule FT-sub|∆|; |Γ|, self : |A| ⊢ Ei self ⇒ |Bi |By rule FT-app|∆|; |Γ|, self : |A| ⊢ (E1 self) , , .. , , (En self) ⇒ |B1 | & .. & |Bn |By rule FT-merge|∆|; |Γ|, self : |A| ⊢ (E1 self) , , .. , , (En self) ⇐ |A|

By rule FT-sub|∆|; |Γ| ⊢ letrec self : |A| = (E1 self) , , .. , , (En self) in self ⇒ |A|190

•ST-trait∆; Γ, self : B ⊢ Ti ⇒ Trait [Bi , Ci ] ⇝ Ei∆; Γ, self : B ⊢ { lj = Tj ′B <: Bii∈1..nj∈1..m∆ ⊢ C1 ∗ .. ∗ Cn ∗ C∆; Γ ⊢ trait [self : B] inherits Ti

i∈1..n{ lj = T j

λself : |B|. (((Ei self)i∈1..n} ⇒ C⇝E

C1 & .. & Cn & C <: Aj∈1..m′} : A ⇒ Trait [B, A] ⇝i∈1..n) , , E)|∆|; |Γ|, self : |B| ⊢ Ei ⇒ |Bi | → |Ci |By i.h.

|∆|; |Γ|, self : |B| ⊢ E ⇒ |C|

By i.h.

|B| <: |Bi |By Lemma 7.2|∆| ⊢ |C1 | ∗ .. ∗ |Cn | ∗ |C|

By Lemma 7.1|C1 | & .. & |Cn | & |C| <: |A|

By Lemma 7.2|∆|; |Γ|, self : |B| ⊢ self ⇒ |B|

By rule FT-var|∆|; |Γ|, self : |B| ⊢ self ⇐ |Bi |By rule FT-sub|∆|; |Γ|, self : |B| ⊢ Ei self ⇒ |Ci |By rule TI-app|∆|; |Γ|, self : |B| ⊢ (E1 self) , , .. , , (En self) , , E ⇒ |C1 | & .. & |Cn | & |C|

By rule FT-merge|∆|; |Γ|, self : |B| ⊢ (E1 self) , , .. , , (En self) , , E ⇒ |A|

By rule FT-sub|∆|; |Γ| ⊢ λself : |B|. (E1 self) , , .. , , (En self) , , E ⇒ |B| → |A|•

ST-forward∆; Γ ⊢ T1 ⇒ Trait [A, B] ⇝ E1∆; Γ ⊢ T2 ⇐ A ⇝ E2∆; Γ ⊢ T1 ^ T2 ⇒ B ⇝ E1 E2|∆|; |Γ| ⊢ E1 ⇒ |A| → |B|

By i.h.

|∆|; |Γ| ⊢ E2 ⇐ |A|

By i.h.

|∆|; |Γ| ⊢ E1 E2 ⇒ |B|

By rule FT-app•

ST-abs∆⊢A∆; Γ, x : A ⊢ T ⇐ B ⇝ E∆; Γ ⊢ λx.T ⇐ A → B ⇝ λx. E|∆| ⊢ |A|

By Lemma 7.1|∆|; |Γ|, x : |A| ⊢ E ⇐ |B|

By i.h.

191

C Proofs about SEDEL|∆|; |Γ| ⊢ λx. E ⇐ |A| → |B|•

By rule FT-abs

ST-sub∆; Γ ⊢ T ⇒ A ⇝ E

A <: B∆; Γ ⊢ T ⇐ B ⇝ E|∆|; |Γ| ⊢ E ⇒ |A|

By i.h.

|A| <: |B|

By Lemma 7.2|∆| ⊢ |B|

By Lemma 7.1|∆|; |Γ| ⊢ E ⇐ |B|

By rule FT-sub

192

∆⊢B

D λ Typing Rules, in Full+i

A <: B ⇝ co

(Declarative subtyping)
S-trans

S-reflA2 <: A3 ⇝ co1

A <: A ⇝ id

A1 <: A2 ⇝ co2

S-top

A1 <: A3 ⇝ co1 ◦ co2

A <: ⊤ ⇝ top

S-andL

S-andR

A1 & A2 <: A1 ⇝ π1

A1 & A2 <: A2 ⇝ π2

S-and

S-arr

A1 <: A2 ⇝ co1

A1 <: A3 ⇝ co2

B1 <: A1 ⇝ co1

A1 <: A2 & A3 ⇝ ⟨co1 , co2 ⟩S-rcd

A2 <: B2 ⇝ co2

A1 → A2 <: B1 → B2 ⇝ co1 → co2

S-distArr

A <: B ⇝ co{l : A} <: {l : B} ⇝ co

(A1 → A2 ) & (A1 → A3 ) <: A1 → A2 & A3 ⇝ dist→

S-distRcd

S-topArr

S-topRcd{l : A} & {l : B} <: {l : A & B} ⇝ id

⊤ <: ⊤ → ⊤ ⇝ top→

⊤ <: {l : ⊤} ⇝ id

A∗B

(Disjointness)

D-topL

D-topR⊤∗A

A∗⊤

D-andLA2 ∗ B 2

A ∗ B2A ∗ B1 & B2

A1 ∗ BA1 → A2 ∗ B1 → B2
D-rcdEq

D-andRA ∗ B1D-arr

A2 ∗ B

A1 & A2 ∗ B

D-rcdNeq

D-ax

A∗B

l1 ̸= l2A ∗ax B{l : A} ∗ {l : B}

{l1 : A} ∗ {l2 : B}

A∗B

193

D λ+
i Typing Rules, in Full
A ∗ax B

(Disjointness axioms)

Dax-symB ∗ax A

Dax-intArr

Dax-intRcd

Dax-arrRcd

A ∗ax BInt ∗ax A1 → A2

Int ∗ax {l : A}

A1 → A2 ∗ax {l : B}

Γ⊢E⇒A⇝e(Inference)
T-var

T-top

T-lit

Γ ⊢ ⊤ ⇒ ⊤ ⇝ ⟨⟩

Γ ⊢ i ⇒ Int ⇝ i(x : A) ∈ ΓΓ⊢x⇒A⇝xT-annoT-app

Γ ⊢ E1 ⇒ A1 → A2 ⇝ e1

Γ ⊢ E2 ⇐ A1 ⇝ e2

Γ⊢E⇐A⇝eΓ ⊢ E1 E2 ⇒ A2 ⇝ e1 e2

Γ⊢E:A⇒A⇝e

T-projT-mergeΓ ⊢ E ⇒ {l : A} ⇝ eΓ ⊢ E1 ⇒ A1 ⇝ e1

Γ ⊢ E.l ⇒ A ⇝ eΓ ⊢ E2 ⇒ A2 ⇝ e2

A1 ∗ A2

Γ ⊢ E1 , , E2 ⇒ A1 & A2 ⇝ ⟨e1 , e2 ⟩T-rcd

Γ⊢E⇒A⇝eΓ ⊢ {l = E} ⇒ {l : A} ⇝ eΓ⊢E⇐A⇝e(Checking)

T-abs

T-sub

Γ, x : A ⊢ E ⇐ B ⇝ eΓ⊢E⇒A⇝eΓ ⊢ λx. E ⇐ A → B ⇝ λx. eΓ ⊢ E ⇐ B ⇝ co e

Q ⊢ A ≺: B ⇝ co
A-int

(Algorithmic subtyping)A-and

Q ⊢ A ≺: B1 ⇝ co1[] ⊢ Int ≺: Int ⇝ id
A-arr

Q ⊢ A ≺: B2 ⇝ co2

Q ⊢ A ≺: B1 & B2 ⇝ JQK& ◦ ⟨co1 , co2 ⟩A-rcd

Q, B1 ⊢ A ≺: B2 ⇝ co

Q, l ⊢ A ≺: B ⇝ co

Q ⊢ A ≺: B1 → B2 ⇝ co

Q ⊢ A ≺: {l : B} ⇝ co194

A <: B ⇝ co

A-top

Q ⊢ A ≺: ⊤ ⇝ JQK⊤ ◦ top

A-rcdInt

A-arrInt[] ⊢ A ≺: A1 ⇝ co1

Q ⊢ A2 ≺: Int ⇝ co2

Q ⊢ A ≺: Int ⇝ co

A, Q ⊢ A1 → A2 ≺: Int ⇝ co1 → co2
A-andIntl, Q ⊢ {l : A} ≺: Int ⇝ co
A-andInt

Q ⊢ A1 ≺: Int ⇝ co

Q ⊢ A2 ≺: Int ⇝ co

Q ⊢ A1 & A2 ≺: Int ⇝ co ◦ π1

Q ⊢ A1 & A2 ≺: Int ⇝ co ◦ π2

C : (Γ ⇒ A) 7→ (Γ′ ⇒ B) ⇝ D(Context typing I)

CTyp-empty

[·] : (Γ ⇒ A) 7→ (Γ ⇒ A) ⇝ [·]CTyp-appL

C : (Γ ⇒ A) 7→ (Γ′ ⇒ A1 → A2 ) ⇝ D

Γ′ ⊢ E2 ⇐ A1 ⇝ e

C E2 : (Γ ⇒ A) 7→ (Γ′ ⇒ A2 ) ⇝ D e
CTyp-appRΓ′ ⊢ E1 ⇒ A1 → A2 ⇝ e

C : (Γ ⇒ A) 7→ (Γ′ ⇐ A1 ) ⇝ D

E1 C : (Γ ⇒ A) 7→ (Γ′ ⇒ A2 ) ⇝ e D
CTyp-mergeL

C : (Γ ⇒ A) 7→ (Γ′ ⇒ A1 ) ⇝ D

Γ′ ⊢ E2 ⇒ A2 ⇝ e

A 1 ∗ A2

C , , E2 : (Γ ⇒ A) 7→ (Γ′ ⇒ A1 & A2 ) ⇝ ⟨D, e⟩
CTyp-mergeRΓ′ ⊢ E1 ⇒ A1 ⇝ e

C : (Γ ⇒ A) 7→ (Γ′ ⇒ A2 ) ⇝ D

A1 ∗ A2

E1 , , C : (Γ ⇒ A) 7→ (Γ′ ⇒ A1 & A2 ) ⇝ ⟨e, D⟩
CTyp-rcdCTyp-proj′C : (Γ ⇒ A) 7→ (Γ ⇒ B) ⇝ D

C : (Γ ⇒ A) 7→ (Γ′ ⇒ {l : B}) ⇝ D{l = C} : (Γ ⇒ A) 7→ (Γ′ ⇒ {l : B}) ⇝ D

C.l : (Γ ⇒ A) 7→ (Γ′ ⇒ B) ⇝ D

CTyp-anno

C : (Γ ⇒ B) 7→ (Γ′ ⇐ A) ⇝ D

C : A : (Γ ⇒ B) 7→ (Γ′ ⇒ A) ⇝ D

195

D λ+
i Typing Rules, in Full
C : (Γ ⇐ A) 7→ (Γ′ ⇐ B) ⇝ D(Context typing II)

CTyp-empty

[·] : (Γ ⇐ A) 7→ (Γ ⇐ A) ⇝ [·]CTyp-abs

C : (Γ ⇐ A) 7→ (Γ′ , x : A1 ⇐ A2 ) ⇝ D

x∈
/ Γ′

λx. C : (Γ ⇐ A) 7→ (Γ′ ⇐ A1 → A2 ) ⇝ λx. D
C : (Γ ⇐ A) 7→ (Γ′ ⇒ B) ⇝ D(Context typing III)

CTyp-appL

C : (Γ ⇐ A) 7→ (Γ′ ⇒ A1 → A2 ) ⇝ D

Γ′ ⊢ E2 ⇐ A1 ⇝ e

C E2 : (Γ ⇐ A) 7→ (Γ′ ⇒ A2 ) ⇝ D e
CTyp-appRΓ′ ⊢ E1 ⇒ A1 → A2 ⇝ e

C : (Γ ⇐ A) 7→ (Γ′ ⇐ A1 ) ⇝ D

E1 C : (Γ ⇐ A) 7→ (Γ′ ⇒ A2 ) ⇝ e D
CTyp-mergeL

C : (Γ ⇐ A) 7→ (Γ′ ⇒ A1 ) ⇝ D

Γ′ ⊢ E2 ⇒ A2 ⇝ e

A 1 ∗ A2

C , , E2 : (Γ ⇐ A) 7→ (Γ′ ⇒ A1 & A2 ) ⇝ ⟨D, e⟩
CTyp-mergeRΓ′ ⊢ E1 ⇒ A1 ⇝ e

C : (Γ ⇐ A) 7→ (Γ′ ⇒ A2 ) ⇝ D

A1 ∗ A2′

E1 , , C : (Γ ⇐ A) 7→ (Γ ⇒ A1 & A2 ) ⇝ ⟨e, D⟩
CTyp-projCTyp-rcdC : (Γ ⇐ A) 7→ (Γ′ ⇒ B) ⇝ D

C : (Γ ⇐ A) 7→ (Γ′ ⇒ {l : B}) ⇝ D{l = C} : (Γ ⇐ A) 7→ (Γ′ ⇒ {l : B}) ⇝ D

C.l : (Γ ⇐ A) 7→ (Γ′ ⇒ B) ⇝ D

CTyp-anno

C : (Γ ⇐ B) 7→ (Γ′ ⇐ A) ⇝ D

C : A : (Γ ⇐ B) 7→ (Γ′ ⇒ A) ⇝ D
C : (Γ ⇒ A) 7→ (Γ′ ⇐ B) ⇝ D
CTyp-abs

C : (Γ ⇒ A) 7→ (Γ′ , x : A1 ⇐ A2 ) ⇝ D(Context typing IV)x∈/ Γ′

λx. C : (Γ ⇒ A) 7→ (Γ′ ⇐ A1 → A2 ) ⇝ λx. D

196

D.1 λco Typing Rules

D. λco Typing Rules(Coercion typing)co :: τ1 ▷ τ2
ct-trans

ct-reflco1 :: τ2 ▷ τ3

id :: τ ▷ τ
ct-arrco1 :: τ1′ ▷ τ1

co2 :: τ1 ▷ τ2

co1 ◦ co2 :: τ1 ▷ τ3

ct-topct-topArr

top :: τ ▷ ⟨⟩top→ :: ⟨⟩ ▷ ⟨⟩ → ⟨⟩ct-pairco2 :: τ2 ▷ τ2′

co1 :: τ1 ▷ τ2

co1 → co2 :: τ1 → τ2 ▷ τ1′ → τ2′

ct-projl

co2 :: τ1 ▷ τ3

⟨co1 , co2 ⟩ :: τ1 ▷ τ2 × τ3

π1 :: τ1 × τ2 ▷ τ1

ct-projr

ct-distArr

π2 :: τ1 × τ2 ▷ τ2

dist→ :: (τ1 → τ2 ) × (τ1 → τ3 ) ▷ τ1 → τ2 × τ3e −→ e′(Single-step reduction)

r-id

r-trans

r-top

r-topArr

id v −→ v(co1 ◦ co2 ) v −→ co1 (co2 v)

top v −→ ⟨⟩(top→ ⟨⟩) ⟨⟩ −→ ⟨⟩r-pairr-arr⟨co1 , co2 ⟩ v −→ ⟨co1 v, co2 v⟩((co1 → co2 ) v1 ) v2 −→ co2 (v1 (co1 v2 ))r-distArr

r-projlr-projr(dist→ ⟨v1 , v2 ⟩) v3 −→ ⟨v1 v3 , v2 v3 ⟩π1 ⟨v1 , v2 ⟩ −→ v1

π2 ⟨v1 , v2 ⟩ −→ v2

r-app(λx. e) v −→ [v/x]e

r-ctxte −→ e′E[e] −→ E[e′ ]197

E F Typing Rules, in Full
+
i

∆⊢A

(Well-formedness of types)

swft-topswft-int∆⊢⊤∆ ⊢ Int

swft-all∆⊢A∆, α ∗ A ⊢ B

swft-varswft-arrow(α ∗ A) ∈ ∆

∆⊢A

∆⊢α

∆⊢A→B

swft-andswft-rcd∆⊢A∆ ⊢ ∀(α ∗ A). B

∆⊢B

∆⊢B

∆⊢A

∆ ⊢ A&B∆⊢Γ∆ ⊢ {l : A}

(Well-formedness of value context)
swfe-varswfe-empty

∆⊢Γ

∆⊢•

∆⊢A

∆ ⊢ Γ, x : A⊢∆(Well-formedness of type context)
swfte-varswfte-empty

⊢∆

⊢•

∆⊢A

⊢ ∆, α ∗ A

A <: B ⇝ co

(Declarative subtyping)
S-trans

S-reflA2 <: A3 ⇝ co1

A <: A ⇝ id
S-rcd

A <: B ⇝ co{l : A} <: {l : B} ⇝ co

A1 <: A2 ⇝ co2

A1 <: A3 ⇝ co1 ◦ co2

S-top

A <: ⊤ ⇝ top

S-arr

B1 <: A1 ⇝ co1

A2 <: B2 ⇝ co2

A1 → A2 <: B1 → B2 ⇝ co1 → co2

199

E F+
i Typing Rules, in Full
S-forall

S-andLA2 <: A1 ⇝ co′B1 <: B2 ⇝ co∀(α ∗ A1 ). B1 <: ∀(α ∗ A2 ). B2 ⇝ co∀

A1 & A2 <: A1 ⇝ π1

S-and

S-andRA1 <: A2 ⇝ co1

A1 & A2 <: A2 ⇝ π2

A1 <: A3 ⇝ co2

A1 <: A2 & A3 ⇝ ⟨co1 , co2 ⟩S-topArr

S-topRcd

S-topAll⊤ <: ⊤ → ⊤ ⇝ top→

⊤ <: {l : ⊤} ⇝ id

⊤ <: ∀(α ∗ ⊤). ⊤ ⇝ top∀

S-distArr(A1 → A2 ) & (A1 → A3 ) <: A1 → A2 & A3 ⇝ dist→
S-distRcd{l : A} & {l : B} <: {l : A & B} ⇝ id
S-distAll(∀(α ∗ A). B1 ) & (∀(α ∗ A). B2 ) <: ∀(α ∗ A). B1 & B2 ⇝ dist∀∆⊢A∗B(Disjointness)

FD-topL

FD-topR⌉A⌈⌉B⌈∆ ⊢ A2 ∗ B 2

∆⊢A∗B

∆⊢A∗B

∆ ⊢ A1 → A2 ∗ B1 → B2

FD-andR

FD-rcdEq

FD-andL∆ ⊢ A1 ∗ B∆ ⊢ A2 ∗ B∆ ⊢ A1 & A2 ∗ B
FD-rcdNeq

FD-arr∆ ⊢ A ∗ B1

∆ ⊢ A ∗ B1 & B2
FD-tvarL

l1 ̸= l2(α ∗ A) ∈ ∆∆ ⊢ {l1 : A} ∗ {l2 : B}

∆ ⊢ {l : A} ∗ {l : B}
FD-tvarR

A <: B(α ∗ A) ∈ ∆∆⊢α∗BFD-forall

200

∆⊢A∗B

∆ ⊢ A ∗ B2

∆⊢B∗α
FD-ax

∆, α ∗ A1 & A2 ⊢ B1 ∗ B2

A ∗ax B∆ ⊢ ∀(α ∗ A1 ). B1 ∗ ∀(α ∗ A2 ). B2∆⊢A∗B

A <: BA ∗ax B(Disjointness axioms)

Dax-symB ∗ax A

Dax-intArr

Dax-intRcd

Dax-intAll

A ∗ax BInt ∗ax A1 → A2

Int ∗ax {l : A}

Int ∗ax ∀(α ∗ B1 ). B2

Dax-arrAll

Dax-arrRcd

Dax-allRcd

A1 → A2 ∗ax ∀(α ∗ B1 ). B2

A1 → A2 ∗ax {l : B}∀(α ∗ A1 ). A2 ∗ax {l : B}∆; Γ ⊢ E ⇒ A ⇝ e(Inference)

FT-topFT-var

FT-int

⊢∆

∆⊢Γ

⊢∆

∆; Γ ⊢ ⊤ ⇒ ⊤ ⇝ ⟨⟩

∆⊢Γ

⊢∆

∆⊢Γ

∆; Γ ⊢ i ⇒ Int ⇝ i(x : A) ∈ Γ∆; Γ ⊢ x ⇒ A ⇝ x

FT-app∆; Γ ⊢ E1 ⇒ A1 → A2 ⇝ e1∆; Γ ⊢ E2 ⇐ A1 ⇝ e2∆; Γ ⊢ E1 E2 ⇒ A2 ⇝ e1 e2
FT-merge∆; Γ ⊢ E1 ⇒ A1 ⇝ e1∆; Γ ⊢ E2 ⇒ A2 ⇝ e2

∆ ⊢ A1 ∗ A2∆; Γ ⊢ E1 , , E2 ⇒ A1 & A2 ⇝ ⟨e1 , e2 ⟩FT-annoFT-tabs∆; Γ ⊢ E ⇐ A ⇝ e∆, α ∗ A; Γ ⊢ E ⇒ B ⇝ e∆; Γ ⊢ E : A ⇒ A ⇝ e

∆⊢A

∆⊢Γ

∆; Γ ⊢ Λ(α ∗ A). E ⇒ ∀(α ∗ A). B ⇝ Λα. e
FT-rcd

FT-tapp∆; Γ ⊢ E ⇒ ∀(α ∗ B). C ⇝ e∆⊢A∗B∆; Γ ⊢ E A ⇒ [A/α]C ⇝ e |A|∆; Γ ⊢ E ⇒ A ⇝ e∆; Γ ⊢ {l = E} ⇒ {l : A} ⇝ e

FT-proj∆; Γ ⊢ E ⇒ {l : A} ⇝ e∆; Γ ⊢ E.l ⇒ A ⇝ e

201

E F+
i Typing Rules, in Full∆; Γ ⊢ E ⇐ A ⇝ e(Checking)FT-abs∆⊢A∆; Γ, x : A ⊢ E ⇐ B ⇝ e∆; Γ ⊢ λx. E ⇐ A → B ⇝ λx. e
FT-sub∆; Γ ⊢ E ⇒ B ⇝ e

B <: A ⇝ co∆⊢A∆; Γ ⊢ E ⇐ A ⇝ co e
Q ⊢ A ≺: B ⇝ co

(Algorithmic subtyping)
A-and

A-constQ ⊢ A ≺: B1 ⇝ co1[] ⊢ c ≺: c ⇝ id

Q ⊢ A ≺: B2 ⇝ co2

Q ⊢ A ≺: B1 & B2 ⇝ JQK& ◦ ⟨co1 , co2 ⟩
A-rcd

A-arr

A-top

Q, B1 ⊢ A ≺: B2 ⇝ co

Q, l ⊢ A ≺: B ⇝ co

Q ⊢ A ≺: B1 → B2 ⇝ co

Q ⊢ A ≺: {l : B} ⇝ co

Q ⊢ A ≺: ⊤ ⇝ JQK⊤ ◦ top

A-arrConst

A-forallQ, α ∗ B1 ⊢ A ≺: B2 ⇝ co
Q ⊢ A ≺: ∀(α ∗ B1 ). B2 ⇝ co
A-rcdConst[] ⊢ A ≺: A1 ⇝ co1

Q ⊢ A2 ≺: c ⇝ co2

A, Q ⊢ A1 → A2 ≺: c ⇝ co1 → co2
A-andConst

Q ⊢ A ≺: c ⇝ co

Q ⊢ A1 ≺: c ⇝ col, Q ⊢ {l : A} ≺: c ⇝ co

Q ⊢ A1 & A2 ≺: c ⇝ co ◦ π1

A-andConstA-allConst

Q ⊢ A2 ≺: c ⇝ co
Q ⊢ A1 & A2 ≺: c ⇝ co ◦ π2[] ⊢ A ≺: A1

Q ⊢ A2 ≺: c ⇝ co(α ∗ A, Q) ⊢ ∀(α ∗ A1 ). A2 ≺: c ⇝ co∀

C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ B) ⇝ D(Context typing I)

FCTyp-empty

[·] : (∆; Γ ⇒ A) 7→ (∆; Γ ⇒ A) ⇝ [·]FCTyp-appL

C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ A1 → A2 ) ⇝ D∆′ ; Γ′ ⊢ E2 ⇐ A1 ⇝ e

C E2 : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ A2 ) ⇝ D e

202

FCTyp-appR

C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇐ A1 ) ⇝ D∆′ ; Γ′ ⊢ E1 ⇒ A1 → A2 ⇝ e

E1 C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ A2 ) ⇝ e D
FCTyp-mergeL

C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ A1 ) ⇝ D∆′ ; Γ′ ⊢ E2 ⇒ A2 ⇝ e∆′ ⊢ A1 ∗ A2

C , , E2 : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ A1 & A2 ) ⇝ ⟨D, e⟩
FCTyp-mergeR

C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ A2 ) ⇝ D∆′ ; Γ′ ⊢ E1 ⇒ A1 ⇝ e∆′ ⊢ A1 ∗ A2

E1 , , C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ A1 & A2 ) ⇝ ⟨e, D⟩
FCTyp-rcd

C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ B) ⇝ D{l = C} : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ {l : B}) ⇝ D
FCTyp-projC : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ {l : B}) ⇝ D
C.l : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ B) ⇝ D
FCTyp-anno

C : (∆; Γ ⇒ B) 7→ (∆′ ; Γ′ ⇐ A) ⇝ D

C : A : (∆; Γ ⇒ B) 7→ (∆′ ; Γ′ ⇒ A) ⇝ D
FCTyp-tabs

C : (∆; Γ ⇒ A) 7→ (∆′ , α ∗ B; Γ′ ⇒ B′ ) ⇝ D∆′ ⊢ B∆′ ⊢ Γ′Λ(α ∗ B). C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ ∀(α ∗ B). B′ ) ⇝ Λα. D
FCTyp-tappC : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ ∀(α ∗ A1 ). A2 ) ⇝ D∆′ ⊢ B∆′ ⊢ B ∗ A1

C B : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇒ [B/α]A2 ) ⇝ D |B|

203

E F+
i Typing Rules, in Full
C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇐ B) ⇝ D(Context typing II)

FCTyp-empty

[·] : (∆; Γ ⇐ A) 7→ (∆; Γ ⇐ A) ⇝ [·]FCTyp-abs

C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ , x : A1 ⇐ A2 ) ⇝ D∆′ ⊢ A1

λx. C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇐ A1 → A2 ) ⇝ λx. D
C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ B) ⇝ D
FCTyp-appL

(Context typing III)C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ A1 → A2 ) ⇝ D∆′ ; Γ′ ⊢ E2 ⇐ A1 ⇝ e

C E2 : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ A2 ) ⇝ D e
FCTyp-appR

C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇐ A1 ) ⇝ D∆′ ; Γ′ ⊢ E1 ⇒ A1 → A2 ⇝ e

E1 C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ A2 ) ⇝ e D
FCTyp-mergeL

C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ A1 ) ⇝ D∆′ ; Γ′ ⊢ E2 ⇒ A2 ⇝ e∆′ ⊢ A1 ∗ A2

C , , E2 : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ A1 & A2 ) ⇝ ⟨D, e⟩
FCTyp-mergeR

C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ A2 ) ⇝ D∆′ ; Γ′ ⊢ E1 ⇒ A1 ⇝ e∆′ ⊢ A1 ∗ A2

E1 , , C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ A1 & A2 ) ⇝ ⟨e, D⟩
FCTyp-rcd

C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ B) ⇝ D{l = C} : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ {l : B}) ⇝ D
FCTyp-projC : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ {l : B}) ⇝ D
C.l : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ B) ⇝ D
FCTyp-anno

C : (∆; Γ ⇐ B) 7→ (∆′ ; Γ′ ⇐ A) ⇝ D

C : A : (∆; Γ ⇐ B) 7→ (∆′ ; Γ′ ⇒ A) ⇝ D

204

E.1 Fco Typing Rules
FCTyp-tabs

C : (∆; Γ ⇐ A) 7→ (∆′ , α ∗ B; Γ′ ⇒ B′ ) ⇝ D∆′ ⊢ B∆′ ⊢ Γ′Λ(α ∗ B). C : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ ∀(α ∗ B). B′ ) ⇝ Λα. D
FCTyp-tappC : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ ∀(α ∗ A1 ). A2 ) ⇝ D∆′ ⊢ B∆′ ⊢ B ∗ A1

C B : (∆; Γ ⇐ A) 7→ (∆′ ; Γ′ ⇒ [B/α]A2 ) ⇝ D |B|
C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇐ B) ⇝ D(Context typing IV)

FCTyp-abs

C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ , x : A1 ⇐ A2 ) ⇝ D∆′ ⊢ A1

λx. C : (∆; Γ ⇒ A) 7→ (∆′ ; Γ′ ⇐ A1 → A2 ) ⇝ λx. D

E. Fco Typing RulesΦ⊢Ψ(Well-formedness of value context)
wfe-varwfe-empty

Φ⊢τ

Φ⊢•

Φ⊢Ψ

Φ ⊢ Ψ, x : τΦ⊢τ(Well-formedness of types)

wft-intΦ ⊢ Int

wft-varwft-arrowα∈ΦΦ ⊢ τ1Φ⊢αwft-prodΦ ⊢ τ2Φ ⊢ τ1 → τ2

Φ ⊢ τ1

wft-allΦ ⊢ τ2Φ, α ⊢ τ2Φ ⊢ τ1 × τ2

Φ ⊢ ∀α. τ2(Coercion typing)co :: τ1 ▷ τ2
ct-reflct-trans

co1 :: τ2 ▷ τ3

id :: τ ▷ τ
ct-topArr

top→ :: ⟨⟩ ▷ ⟨⟩ → ⟨⟩co2 :: τ1 ▷ τ2

co1 ◦ co2 :: τ1 ▷ τ3
ct-arrco1 :: τ1′ ▷ τ1

ct-toptop :: τ ▷ ⟨⟩co2 :: τ2 ▷ τ2′

co1 → co2 :: τ1 → τ2 ▷ τ1′ → τ2′

205

E F+
i Typing Rules, in Full
ct-pair

co1 :: τ1 ▷ τ2

co2 :: τ1 ▷ τ3

⟨co1 , co2 ⟩ :: τ1 ▷ τ2 × τ3
ct-forall

ct-projl

ct-projr

π1 :: τ1 × τ2 ▷ τ1

π2 :: τ1 × τ2 ▷ τ2

ct-distArr

co :: τ1 ▷ τ2co∀ :: ∀α. τ1 ▷ ∀α. τ2dist→ :: (τ1 → τ2 ) × (τ1 → τ3 ) ▷ τ1 → τ2 × τ3

ct-topAll

ct-distAll

top∀ :: ⟨⟩ ▷ ∀α. ⟨⟩dist∀ :: (∀α. τ1 ) × (∀α. τ2 ) ▷ ∀α. τ1 × τ2

Φ; Ψ ⊢ e : τ(Static semantics)

Ft-unitFt-varFt-int

Φ⊢Ψ

Φ⊢Ψ

Φ⊢Ψ

Φ; Ψ ⊢ ⟨⟩ : ⟨⟩Φ; Ψ ⊢ i : Int

Ft-abs(x : τ ) ∈ ΨΦ; Ψ ⊢ x : τ

Ft-appΦ; Ψ, x : τ1 ⊢ e : τ2

Φ; Ψ ⊢ e1 : τ1 → τ2

Φ ⊢ τ1

Φ; Ψ ⊢ λx. e : τ1 → τ2

Φ; Ψ ⊢ e2 : τ1

Φ; Ψ ⊢ e1 e2 : τ2

Ft-tabs

Ft-tappΦ, α; Ψ ⊢ e : τΦ; Ψ ⊢ e : ∀α. τ ′

Φ⊢Ψ

Φ⊢τ

Φ; Ψ ⊢ e τ : [τ /α]τ ′Φ; Ψ ⊢ Λα. e : ∀α. τ
Ft-cappFt-pairΦ; Ψ ⊢ e1 : τ1

Φ; Ψ ⊢ e2 : τ2

Φ; Ψ ⊢ e : τ1

Φ; Ψ ⊢ ⟨e1 , e2 ⟩ : τ1 × τ2

co :: τ1 ▷ τ2

Φ ⊢ τ2

Φ; Ψ ⊢ co e : τ2e −→ e′(Single-step reduction)

r-id

r-trans

r-top

r-topArr

id v −→ v(co1 ◦ co2 ) v −→ co1 (co2 v)

top v −→ ⟨⟩(top→ ⟨⟩) ⟨⟩ −→ ⟨⟩r-pairr-arr⟨co1 , co2 ⟩ v −→ ⟨co1 v, co2 v⟩((co1 → co2 ) v1 ) v2 −→ co2 (v1 (co1 v2 ))r-distArr

r-projlr-projr(dist→ ⟨v1 , v2 ⟩) v3 −→ ⟨v1 v3 , v2 v3 ⟩π1 ⟨v1 , v2 ⟩ −→ v1

π2 ⟨v1 , v2 ⟩ −→ v2

206E.1 Fco Typing Rules
r-forallr-topAllr-distAll(co∀ v) τ −→ co (v τ )(top∀ ⟨⟩) τ −→ ⟨⟩(dist∀ ⟨v1 , v2 ⟩) τ −→ ⟨v1 τ, v2 τ ⟩r-tappr-app(Λα. e) τ −→ [τ /α]e(λx. e) v −→ [v/x]e

r-ctxte −→ e′E[e] −→ E[e′ ]207F SEDEL Typing Rules, in Full∆⊢A

(Well-formedness of types)

WF-topWF-int∆⊢⊤∆ ⊢ Int

∆⊢A

∆⊢B

∆⊢A→B

∆⊢B

∆⊢A

∆ ⊢ A&B

WF-var∆⊢A(α ∗ A) ∈ ∆∆ ⊢ {l : A}∆⊢α

WF-forall

WF-and∆⊢A

WF-rcdWF-arrWF-trait∆, α ∗ A ⊢ B∆ ⊢ ∀(α ∗ A). B

∆⊢A

∆⊢B

∆ ⊢ Trait [A, B]

A <: B(Subtyping)

TS-reflTS-trans

A2 <: A3

A <: AA1 <: A3

A <: ⊤

TS-arr

B1 <: A1A2 <: B2

A1 → A2 <: B1 → B2
TS-andA1 <: A2

TS-topA1 <: A2

TS-rcdA <: B{l : A} <: {l : B}

TS-andL

TS-andR

A1 & A2 <: A1

A1 & A2 <: A2

TS-distArr

A1 <: A3A1 <: A2 & A3

(A1 → A2 ) & (A1 → A3 ) <: A1 → A2 & A3

TS-topArr

TS-distRcd

TS-topRcd⊤ <: ⊤ → ⊤{l : A} & {l : B} <: {l : A & B}

⊤ <: {l : ⊤}TS-trait

B1 <: A1A2 <: B2

Trait [A1 , A2 ] <: Trait [B1 , B2 ]209

F SEDEL Typing Rules, in Full∆⊢A∗B

(Disjointness)

SD-topL

SD-topR

∆⊢⊤∗A

∆⊢A∗⊤

SD-arr∆ ⊢ A 2 ∗ B2
∆ ⊢ A 1 → A 2 ∗ B1 → B 2

SD-andL

SD-andR∆ ⊢ A1 ∗ B∆ ⊢ A2 ∗ B∆ ⊢ A ∗ B1

∆ ⊢ A1 & A2 ∗ B
SD-rcdEq∆ ⊢ A ∗ B2

∆ ⊢ A ∗ B1 & B2

SD-rcdNeq

SD-tvarL∆⊢A∗B

l1 ̸= l2

∆ ⊢ {l : A} ∗ {l : B}

∆ ⊢ {l1 : A} ∗ {l2 : B}

SD-tvarR(α ∗ A) ∈ ∆A <: B∆⊢α∗B

SD-forall(α ∗ A) ∈ ∆A <: B∆, α ∗ A1 & A2 ⊢ B1 ∗ B2∆⊢B∗α∆ ⊢ ∀(α ∗ A1 ). B1 ∗ ∀(α ∗ A2 ). B2

SD-trait

SD-traitArr

∆ ⊢ A 2 ∗ B2

∆ ⊢ A 2 ∗ B2

∆ ⊢ Trait [A1 , A2 ] ∗ Trait [B1 , B2 ]∆ ⊢ Trait [A1 , A2 ] ∗ B1 → B2

SD-traitArr

SD-ax∆ ⊢ A 2 ∗ B2

A ∗ax B∆ ⊢ A1 → A2 ∗ Trait [B1 , B2 ]∆⊢A∗B

A ∗ax B

(Disjointness axiom)

SDax-sym

B ∗ax A

SDax-intArr

SDax-intRcd

SDax-intAll

A ∗ax BInt ∗ax A1 → A2

Int ∗ax {l : A}

Int ∗ax ∀(α ∗ B1 ). B2

SDax-arrAll

SDax-arrRcd

SDax-allRcd

A1 → A2 ∗ax ∀(α ∗ B1 ). B2

A1 → A2 ∗ax {l : B}∀(α ∗ A1 ). A2 ∗ax {l : B}

SDax-intTrait

SDax-traitAll

Int ∗ax Trait [A1 , A2 ]Trait [A1 , A2 ] ∗ax ∀(α ∗ B1 ). B2

SDax-traitRcd

Trait [A1 , A2 ] ∗ax {l : B}

210

∆; Γ ⊢ T ⇒ A ⇝ E(Inference)
ST-varST-topST-int∆; Γ ⊢ ⊤ ⇒ ⊤ ⇝ ⊤∆; Γ ⊢ i ⇒ Int ⇝ i(x : A) ∈ ∆∆; Γ ⊢ x ⇒ A ⇝ x

ST-app∆; Γ ⊢ T1 ⇒ A1 → A2 ⇝ E1∆; Γ ⊢ T2 ⇐ A1 ⇝ E2∆; Γ ⊢ T1 T2 ⇒ A2 ⇝ E1 E2
ST-merge∆; Γ ⊢ T1 ⇒ A ⇝ E1∆; Γ ⊢ T2 ⇒ B ⇝ E2∆⊢A∗B∆; Γ ⊢ T1 , , T2 ⇒ A & B ⇝ E1 , , E2
ST-anno∆; Γ ⊢ T ⇐ A ⇝ E∆; Γ ⊢ T : A ⇒ A ⇝ E : |A|
ST-tabs∆⊢A∆, α ∗ A; Γ ⊢ T ⇒ B ⇝ E∆; Γ ⊢ Λ(α ∗ A). T ⇒ ∀(α ∗ A). B ⇝ Λ(α ∗ |A|). E
ST-tapp∆; Γ ⊢ T ⇒ ∀(α ∗ B1 ). B2 ⇝ E∆⊢A∆ ⊢ A ∗ B1∆; Γ ⊢ T A ⇒ [A/α]B2 ⇝ E |A|
ST-proj

ST-rcd∆; Γ ⊢ T ⇒ A ⇝ E∆; Γ ⊢ T ⇒ {l : A} ⇝ E∆; Γ ⊢ {l = T } ⇒ {l : A} ⇝ {l = E}∆; Γ ⊢ T .l ⇒ A ⇝ E.l

ST-trait∆; Γ, self : B ⊢ Ti ⇒ Trait [Bi , Ci ] ⇝ Ei∆; Γ, self : B ⊢ { lj = Tj ′B <: Bii∈1..nj∈1..m} ⇒ C⇝E∆ ⊢ C1 ∗ .. ∗ Cn ∗ C∆; Γ ⊢ trait [self : B] inherits Ti

i∈1..n{ lj = Tj ′λself : |B|. (((Ei self)i∈1..nC1 & .. & Cn & C <: Aj∈1..mi∈1..n} : A ⇒ Trait [B, A] ⇝) , , E)211

F SEDEL Typing Rules, in Full
ST-new∆; Γ ⊢ Ti ⇒ Trait [Ai , Bi ] ⇝ Ei
A <: Ai

i∈1..n∆; Γ ⊢ new [A]( Ti∆ ⊢ B1 ∗ .. ∗ Bni∈1..ni∈1..nB1 & .. & Bn <: A

) ⇒ A ⇝ letrec self : |A| = (Ei self)i∈1..nin self

ST-forward∆; Γ ⊢ T1 ⇒ Trait [A, B] ⇝ E1∆; Γ ⊢ T2 ⇐ A ⇝ E2∆; Γ ⊢ T1 ^ T2 ⇒ B ⇝ E1 E2∆; Γ ⊢ T ⇐ A ⇝ E(Checking)ST-abs∆⊢A∆; Γ, x : A ⊢ T ⇐ B ⇝ E∆; Γ ⊢ λx.T ⇐ A → B ⇝ λx. E
ST-sub∆; Γ ⊢ T ⇒ A ⇝ E

A <: B∆; Γ ⊢ T ⇐ B ⇝ E

212∆⊢B