Higher-rank Polymorphism: Type Inference andExtensions
byNingning Xie(谢宁宁)A thesis submitted in partial fulfillment of the requirements forthe degree of Doctor of Philosophy
at The University of Hong Kong

July 2021

Abstract of thesis entitled“Higher-rank Polymorphism: Type Inference and Extensions”
Submitted by
Ningning Xie
for the degree of Doctor of Philosophy
at The University of Hong Kong
in July 2021
Type inference, as implemented in various modern programming languages, reconstructs
missing types in expressions and increases programmers’ productivity. Modern functional
languages such as Haskell come with powerful forms of type inference. The global typeinference algorithms employed in those languages are derived from the Hindley-Milner typesystem, with multiple extensions. As the languages evolve, researchers also formalize the key
aspects of type inference for the new extensions.
This dissertation studies predicative implicit higher-rank polymorphism, where polymorphic types can be arbitrarily nested, and monomorphic types can be inferred automatically.
Predicative implicit higher-rank polymorphism is a common extension that has been studied extensively in the literature, and has been used pervasively in modern statically typed
programming languages.
The goal of this dissertation is to explore the design space of type inference for implicit
predicative higher-rank polymorphism, as well as to study its integration with other advanced type system features. The first contribution of this dissertation is a new type inference algorithm for implicit higher-rank polymorphism which can accepts programs thatmany existing type inference algorithms cannot. The proposed application mode provides
new insights for bidirectional type checking. The second contribution is the first combination of predicative implicit higher-rank polymorphism with gradual typing, which provides
a step forward in gradualizing modern functional programming languages. The third contribution is an arguably simpler algorithmic implementation of subtyping for higher-rankpolymorphism. The technique developed is then further applied to the kind inference problem for datatypes, which provides a first known formal model of datatype declarations inmodern functional programming languages.

An abstract of 253 words

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.

..............................
Ningning Xie
July 2021iAcknowledgments
I am incredibly fortunate to have many people to thank.
First and foremost, I would like to thank my advisor Bruno C. d. S. Oliveira, who guided
me into the land of functional programming. He was always very patient with me, no matter
what crazy research ideas I would like to try. Every meeting I had with Bruno was fruitful– he could consistently provide insightful suggestions and was also willing to dive deeply
into technical discussions. It is his mentorship and encouragement which made this thesis
possible.
I thank my thesis committee, Zhiyi Huang and Giulio Chiribella, for their careful reading of the draft, and especially Simon Peyton Jones for his 18-page long review and various
constructive feedback – the thesis has improved a lot because of his suggestions.
I would like to thank Tom Schrijvers, who hosted me during my visit to KU Leuven. Tom
is insightful and has the fantastic ability to explain complicated things in an abstract andeasy-to-follow manner. I am also very grateful for his long-lasting support with my research
career.
Richard Eisenberg introduced me to the Haskell community. I greatly enjoyed my summer
stay in Philadelphia when hacking GHC. I always try to learn from him how to communicate
research ideas effectively and how to think practically.
Great thanks to Daan Leijen, who hosted me during my internship at Microsoft Research.
Daan has lots of exciting ideas, and his research vision on Koka has always inspired me. Our
collaboration has been so fruitful, and I expect much more to come. I also thank Madan
Musuvathi and the whole RiSE team for their generous support of this internship whichhappened during the pandemic.
I am grateful to the wonderful programming language friends I have met all over the world.
I had lots of fun with the PLGroup at HKU: Xuan Bi, Xuejing Huang, Huang Li, Baber
Rehman, Tomas Tauber, Yanlin Wang, Yanpeng Yang, Weixin Zhang, Jinxu Zhao, Yaoda
Zhou. Especially Xuan Bi, who has co-authored several papers with me, and his passion for
Haskell was a big inspiration for me during my early Ph.D. years. I also thank Tom’s excellent students Gert-Jan Bottu, George Karachalias, Steven Keuchel, Klara Mardirosian, Koen
Pauwels, Ruben Pieters, Amr Saleh, César Santos, and Alexander Vandenbroucke, who madeiii

my stay at KU Leuven comfortable and fun. Yao Li and Yishuai Li have helped me a lot during my stay in Philadelphia. My other great collaborators include Jonathan Brachthäuser,
Daniel Hillerström, Leonardo de Moura, Alex Reinking, and Philipp Schuster.
Last but not least, I would like to thank my parents, my brother, and Xujie Si for theirunconditional love and support. I made it through thanks to all of you.

iv

Contents
Declarationi

Acknowledgmentsiii

List of Figures

xi

I

Prologue

1

1

Introduction

3

1.1

Preliminaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3

1.1.1

Type Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3

1.1.2

The Hindley-Milner Type System . . . . . . . . . . . . . . . . . . .

4

1.1.3

Higher-rank Polymorphism . . . . . . . . . . . . . . . . . . . . . .

5

1.1.4

Implicit Polymorphism . . . . . . . . . . . . . . . . . . . . . . . .

5

1.1.5

Predicativity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6Contribution Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6

1.2.1

Type Inference for Predicative Implicit Higher-rank Polymorphism

7

1.2.2

Gradually Typed Higher-rank Polymorphism . . . . . . . . . . . .

7

1.2.3

Type Promotion and Kind Inference for Datatypes . . . . . . . . .

9Contributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10

1.2

1.3
2

Background

15

2.1

The Hindley-Milner Type System . . . . . . . . . . . . . . . . . . . . . . .

15

2.1.1

Declarative System . . . . . . . . . . . . . . . . . . . . . . . . . .

15

2.1.2

Principal Type Scheme . . . . . . . . . . . . . . . . . . . . . . . .

17

2.1.3

Algorithmic Type System . . . . . . . . . . . . . . . . . . . . . . .

18

The Odersky-Läufer Type System . . . . . . . . . . . . . . . . . . . . . . .

19

2.2.1

Higher-rank Types . . . . . . . . . . . . . . . . . . . . . . . . . . .

19

2.2.2

Declarative System . . . . . . . . . . . . . . . . . . . . . . . . . .

20

2.2.3

Relating to HM . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

2.2

v

Contents
2.2.4
2.3

Discussion: Variance and Eta-equality . . . . . . . . . . . . . . . .

22

The Dunfield-Krishnaswami Type System . . . . . . . . . . . . . . . . . .

23

2.3.1

Bidirectional Type Checking . . . . . . . . . . . . . . . . . . . . .

23

2.3.2

Declarative System . . . . . . . . . . . . . . . . . . . . . . . . . .

24

2.3.3

Algorithmic Type System . . . . . . . . . . . . . . . . . . . . . . .

28

2.3.4

Discussion: lazy and eager instantiation . . . . . . . . . . . . . . .

28

IIBidirectional Type Checking With the Application Mode

31

3Higher-Rank Polymorphism with the Application Mode

33

3.1

Introduction and Motivation . . . . . . . . . . . . . . . . . . . . . . . . .

33

3.1.1

Revisiting Bidirectional Type Checking . . . . . . . . . . . . . . .

33

3.1.2

Type Checking with The Application Mode . . . . . . . . . . . . .

35

3.1.3

Benefits of Information Flowing from Arguments to Functions . . .

37

3.1.4

Type Inference of Higher-rank Types . . . . . . . . . . . . . . . . .

38

Declarative System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40

3.2.1

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

41

3.2.2

Type System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

3.2.3

Subtyping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

Type-directed Translation . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

3.3.1

Target Language . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

3.3.2

Subtyping Coercions . . . . . . . . . . . . . . . . . . . . . . . . .

48

3.3.3

Type-Directed Translation of Typing . . . . . . . . . . . . . . . . .

51

3.3.4

Type Safety . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

51

3.3.5

Coherence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

52

3.4

Type Inference Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . .

53

3.5

Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

54

3.5.1

Combining Application and Checking Modes . . . . . . . . . . . .

54

3.5.2

Additional Constructs . . . . . . . . . . . . . . . . . . . . . . . . .

55

3.5.3

More Expressive Type Applications . . . . . . . . . . . . . . . . . .

56

3.2

3.3

III Higher-Rank Polymorphism and Gradual Typing

59

4

Gradually Typed Higher-Rank Polymorphism

61

4.1

Introduction and Motivation . . . . . . . . . . . . . . . . . . . . . . . . .

62

4.1.1

62

vi

Background: Gradual Typing . . . . . . . . . . . . . . . . . . . . .

Contents

4.2

4.3

4.4

4.5

5

4.1.2

Motivation: Gradually Typed Higher-Rank Polymorphism . . . . .

64

4.1.3

Application: Efficient (Partly) Typed Encodings of ADTs . . . . . .

65

Revisiting Consistent Subtyping . . . . . . . . . . . . . . . . . . . . . . . .

68

4.2.1

Consistency and Subtyping . . . . . . . . . . . . . . . . . . . . . .

68

4.2.2

Towards Consistent Subtyping . . . . . . . . . . . . . . . . . . . .

71

4.2.3

Abstracting Gradual Typing . . . . . . . . . . . . . . . . . . . . . .

74

4.2.4

Directed Consistency . . . . . . . . . . . . . . . . . . . . . . . . .

75

4.2.5

Consistent Subtyping Without Existentials . . . . . . . . . . . . . .

76

Gradually Typed Implicit Polymorphism . . . . . . . . . . . . . . . . . . .

77

4.3.1

Typing in Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . .

78

4.3.2

Type-directed Translation . . . . . . . . . . . . . . . . . . . . . . .

80

4.3.3

Correctness Criteria . . . . . . . . . . . . . . . . . . . . . . . . . .

83

Algorithmic Type System . . . . . . . . . . . . . . . . . . . . . . . . . . .

85

4.4.1

Algorithmic Consistent Subtyping . . . . . . . . . . . . . . . . . .

87

4.4.2

Instantiation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

89

4.4.3

Algorithmic Typing . . . . . . . . . . . . . . . . . . . . . . . . . .

91

4.4.4

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

93

4.4.5

Context Extension . . . . . . . . . . . . . . . . . . . . . . . . . . .

95

4.4.6

Soundness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

96

4.4.7

Completeness . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

97

Simple Extensions and Variants . . . . . . . . . . . . . . . . . . . . . . . .

98

4.5.1

Top Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

98

4.5.2

A More Declarative Type System . . . . . . . . . . . . . . . . . . . 100

Restoring the Dynamic Gradual Guarantee with Type Parameters

103

5.1

Declarative Type System . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

5.2

Substitutions and Representative Translations . . . . . . . . . . . . . . . . 104

5.3

Dynamic Gradual Guarantee, Reloaded . . . . . . . . . . . . . . . . . . . . 107

5.4

Extended Algorithmic Type System . . . . . . . . . . . . . . . . . . . . . . 108

5.5

5.4.1

Extended Algorithmic Consistent Subtyping . . . . . . . . . . . . . 109

5.4.2

Extended Instantiation . . . . . . . . . . . . . . . . . . . . . . . . 112

5.4.3

Algorithmic Typing and Metatheory . . . . . . . . . . . . . . . . . 112

5.4.4

Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

Restricted Generalization . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

vii

Contents

IV Type Inference with Promotion

117

6

119

Higher-Rank Type Inference Algorithm with Type Promotion
6.1

6.2

6.3

6.4

7

6.1.1

Background:Type Inference in Context . . . . . . . . . . . . . . . . 119

6.1.2

Our Approach: Type Promotion . . . . . . . . . . . . . . . . . . . 122

6.1.3

Polymorphic Promotion . . . . . . . . . . . . . . . . . . . . . . . 123

Unification for the Simply Typed Lambda Calculus . . . . . . . . . . . . . 125
6.2.1

Declarative System . . . . . . . . . . . . . . . . . . . . . . . . . . 126

6.2.2

Algorithmic System . . . . . . . . . . . . . . . . . . . . . . . . . . 126

6.2.3

Soundness and Completeness . . . . . . . . . . . . . . . . . . . . . 130

Subtyping for Higher-Rank Polymorphism . . . . . . . . . . . . . . . . . . 131
6.3.1

Declarative System . . . . . . . . . . . . . . . . . . . . . . . . . . 132

6.3.2

Algorithmic System . . . . . . . . . . . . . . . . . . . . . . . . . . 132

6.3.3

Soundness and Completeness . . . . . . . . . . . . . . . . . . . . . 137

Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
6.4.1

Promoting Dependent Types . . . . . . . . . . . . . . . . . . . . . 139

6.4.2

Promoting Gradual Types . . . . . . . . . . . . . . . . . . . . . . . 140

6.5

Promotion in Action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140

6.6Levels and Generalization . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

Kind Inference for Datatypes

145

7.1

Introduction and Motivation . . . . . . . . . . . . . . . . . . . . . . . . . 145

7.2

Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147

7.3

7.4

viii

Introduction and Motivation . . . . . . . . . . . . . . . . . . . . . . . . . 119

7.2.1

Kind Inference in Haskell98 . . . . . . . . . . . . . . . . . . . . . . 147

7.2.2

Kind Inference in Modern GHC Haskell . . . . . . . . . . . . . . . 148

7.2.3

Desirable Properties for Kind Inference . . . . . . . . . . . . . . . 152

Datatypes in Haskell98 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
7.3.1

Groups and Dependency Analysis . . . . . . . . . . . . . . . . . . 152

7.3.2

Declarative Typing Rules . . . . . . . . . . . . . . . . . . . . . . . 152

Kind Inference for Haskell98 . . . . . . . . . . . . . . . . . . . . . . . . . 154
7.4.1

Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154

7.4.2

Algorithmic Typing Rules . . . . . . . . . . . . . . . . . . . . . . . 155

7.4.3

Defaulting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

7.4.4

Checking Datatype Declarations . . . . . . . . . . . . . . . . . . . 158

7.4.5

Kinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

7.4.6

Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

Contents
7.4.7
7.5

7.6

7.7

7.8

Soundness and Completeness . . . . . . . . . . . . . . . . . . . . . 161

Type Parameters, Principal Kinds and Completeness in Haskell98 . . . . . 162
7.5.1

Type Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162

7.5.2

Principal Kinds and Defaulting . . . . . . . . . . . . . . . . . . . . 163

7.5.3

Completeness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163

Declarative Syntax and Semantics of PolyKinds . . . . . . . . . . . . . . . 164
7.6.1

Groups and Dependency Analysis . . . . . . . . . . . . . . . . . . 164

7.6.2

Checking Programs . . . . . . . . . . . . . . . . . . . . . . . . . . 165

7.6.3

Checking Kinds . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170

Kind Inference for PolyKinds . . . . . . . . . . . . . . . . . . . . . . . . . 171
7.7.1

Algorithmic Program Typing . . . . . . . . . . . . . . . . . . . . . 172

7.7.2

The Quantification Check . . . . . . . . . . . . . . . . . . . . . . . 174

7.7.3

Kinding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175

7.7.4

Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175

7.7.5

Termination . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179

7.7.6

Soundness, Completeness and Principality . . . . . . . . . . . . . . 181

Language Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
7.8.1

Higher-Rank Polymorphism . . . . . . . . . . . . . . . . . . . . . 182

7.8.2

Generalized Algebraic Datatypes (GADTs) . . . . . . . . . . . . . 183

7.8.3

Type Families . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184

V

Epilogue

187

8

Related Work

189

9

8.1

Type Inference for Higher-Rank Types . . . . . . . . . . . . . . . . . . . . 189

8.2

Bidirectional Type Checking . . . . . . . . . . . . . . . . . . . . . . . . . . 190

8.3

Gradual Typing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191

8.4

Gradual Type Systems with Explicit Polymorphism . . . . . . . . . . . . . 192

8.5

Gradual Type Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193

8.6

Haskell and GHC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193

8.7

Unification with dependent types . . . . . . . . . . . . . . . . . . . . . . . 194

Summary and Future Directions

197

9.1

Dependent Type Systems with Application mode . . . . . . . . . . . . . . . 197

9.2

Type Inference for Intersection Type Systems . . . . . . . . . . . . . . . . . 198

9.3

Gradualizing Type Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . 199

ix

Contents
9.4

Generalized Algebraic Datatypes (GADTs) . . . . . . . . . . . . . . . . . . 200

Bibliography

203

VI Technical Appendix

219

A Full Rules for Algorithmic AP

221

B The Extended Algorithmic GPC

225

B.1

Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

B.2

Type System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

C Kind Inference for Datatypes

229

C.1 Other Language Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . 229
C.1.1

Visible Dependent Quantification . . . . . . . . . . . . . . . . . . 229

C.1.2

Datatype Promotion . . . . . . . . . . . . . . . . . . . . . . . . . . 230

C.1.3

Partial Type Signatures . . . . . . . . . . . . . . . . . . . . . . . . 230

C.2 Today’s GHC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
C.2.1

Constraint-Based Type Inference . . . . . . . . . . . . . . . . . . . 231

C.2.2

Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

C.2.3

Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232

C.2.4

Complete User-Supplied Kinds . . . . . . . . . . . . . . . . . . . . 233

C.2.5

Dependency Analysis . . . . . . . . . . . . . . . . . . . . . . . . . 234

C.2.6

Approach to Kind-Checking Datatypes . . . . . . . . . . . . . . . 234

C.2.7

Polymorphic Recursion . . . . . . . . . . . . . . . . . . . . . . . . 235

C.2.8

The Quantification Check . . . . . . . . . . . . . . . . . . . . . . . 236

C.2.9

ScopedSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237

C.2.10 The “Forall-or-Nothing” Rule . . . . . . . . . . . . . . . . . . . . . 238
C.3 Complete Set of Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238

x

C.3.1

Declarative Haskell98 . . . . . . . . . . . . . . . . . . . . . . . . . 238

C.3.2

Algorithmic Haskell98 . . . . . . . . . . . . . . . . . . . . . . . . 239

C.3.3

Declarative PolyKinds . . . . . . . . . . . . . . . . . . . . . . . . . 240

C.3.4

Algorithmic PolyKinds . . . . . . . . . . . . . . . . . . . . . . . . 240

C.3.5

Context Application in PolyKinds . . . . . . . . . . . . . . . . . . 244

C.3.6

Context Extension in PolyKinds . . . . . . . . . . . . . . . . . . . 245

List of Figures
2.1

Syntax and static semantics of the Hindley-Milner type system. . . . . . . .

16

2.2

Declarative subtyping in the Hindley-Milner type system. . . . . . . . . . .

17

2.3

Syntax of the Odersky-Läufer type system. . . . . . . . . . . . . . . . . . .

20

2.4

Well-formedness of types in the Odersky-Läufer type system. . . . . . . . .

21

2.5

Static semantics of the Odersky-Läufer type system. . . . . . . . . . . . . .

21

2.6

Syntax of the Dunfield-Krishnaswami Type System . . . . . . . . . . . . .

25

2.7

Static semantics of the Dunfield-Krishnaswami type system. . . . . . . . .

26

3.1

Syntax of System AP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

3.2

Typing rules of System AP. . . . . . . . . . . . . . . . . . . . . . . . . . . .

42

3.3

Syntax and typing rules of System F. . . . . . . . . . . . . . . . . . . . . . .

48

3.4

Subtyping translation rules of System AP. . . . . . . . . . . . . . . . . . . .

49

3.5

Typing translation rules of System AP. . . . . . . . . . . . . . . . . . . . .

50

3.6

Type erasure and eta-id equality of System F. . . . . . . . . . . . . . . . . .

52

4.1

Subtyping and type consistency in FOb?<: . . . . . . . . . . . . . . . . . . .

62

4.2

Syntax of types, consistency, subtyping and well-formedness of types in declarative GPC. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

69

4.3

Examples that break the original definition of consistent subtyping. . . . . .

72

4.4

Observations of consistent subtyping . . . . . . . . . . . . . . . . . . . . .

73

4.5

Example that is fixed by the new definition of consistent subtyping. . . . . .

74

4.6

Consistent Subtyping for implicit polymorphism. . . . . . . . . . . . . . .

77

4.7

Syntax of expressions and declarative typing of declarative GPC. . . . . .

79

4.8

Less Precision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

84

4.9

Syntax and well-formedness of the algorithmic GPC. . . . . . . . . . . .

86

4.10 Algorithmic consistent subtyping . . . . . . . . . . . . . . . . . . . . . . .

88

4.11 Algorithmic instantiation . . . . . . . . . . . . . . . . . . . . . . . . . . .

90

4.12 Algorithmic typing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

92

4.13 Context extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

96xi

List of Figures
5.1

Syntax of types, and consistent subtyping in the extended declarative system. 104

5.2

Syntax of types, contexts and consistent subtyping in the extended algorithmic system. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108

5.3

Extended algorithmic consistent subtyping . . . . . . . . . . . . . . . . . . 110

5.4

Instantiation in the extended algorithmic system . . . . . . . . . . . . . . . 113

6.1

Syntax and declarative typing of the simply typed lambda calculus. . . . . . 126

6.2

Syntax of algorithmic STLC . . . . . . . . . . . . . . . . . . . . . . . . . . 127

6.3

Algorithmic typing, promotion and unification of algorithmic STLC . . . . 128

6.4

Context extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

6.5

Syntax and subtyping for higher-rank polymorphism . . . . . . . . . . . . 132

6.6

Types, contexts, and well-formedness of the algorithmic system . . . . . . . 133

6.7

Unification, subtyping and polymorphic promotion of the algorithmic system) 135

7.1

Declarative specification of Haskell98 datatype declarations . . . . . . . . . 153

7.2

Algorithmic program typing in Haskell98 . . . . . . . . . . . . . . . . . . 156

7.3

Defaulting in Haskell98. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

7.4

Algorithmic kinding, unification and promotion in Haskell98. . . . . . . . 159

7.5

Context extension in Haskell98 . . . . . . . . . . . . . . . . . . . . . . . . 161

7.6

Syntax of PolyKinds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

7.7

Declarative specification of PolyKinds . . . . . . . . . . . . . . . . . . . . 166

7.8

Auxiliary functions of declarative specification in PolyKinds . . . . . . . . 167

7.9

Declarative kind-checking in PolyKinds . . . . . . . . . . . . . . . . . . . 169

7.10 Declarative elaborated kinding in PolyKinds . . . . . . . . . . . . . . . . . 170
7.11 Algorithmic syntax in PolyKinds . . . . . . . . . . . . . . . . . . . . . . . 171
7.12 Algorithmic program typing in PolyKinds . . . . . . . . . . . . . . . . . . 173
7.13 Unsolved set of unification variables and quantification check in PolyKinds

174

7.14 Selected rules for algorithmic kinding in PolyKinds . . . . . . . . . . . . . 176
7.15 Selected rules for unification, promotion, and moving in PolyKinds . . . . 177
7.16 Example of dependency graph . . . . . . . . . . . . . . . . . . . . . . . . . 180

xii

Part I
Prologue1

1 Introduction
Modern functional languages such as Haskell, ML, and OCaml come with powerful formsof type inference. The global type-inference algorithms employed in those languages are derived from the Hindley-Milner type system (HM) [Damas and Milner 1982; Hindley 1969],
with multiple extensions. As the languages evolve, researchers also formalize the key aspectsof type inference for the new extensions. One common extension of HM, which is also thecentral theme of this dissertation, is higher-rank polymorphism [Dunfield and Krishnaswami
2013; Odersky and Läufer 1996; Peyton Jones et al. 2007]. In particular, we are interested in
predicative implicit higher-rank polymorphism, which extends type inference for functional
programming languages in the presence of polymorphic types.

1.1 Preliminaries
1.1.1 Type Inference
In real world, many programming languages are typed, including C, Java, and most functional programming languages like Haskell. In those languages, numbers like 1,2,3 aregiven type Int, while True and False are given type Bool. With such type information, if
we know that
add : Int → Int → Int

we can accept expressions likeadd 1 2

while correctly rejecting programs likeadd 1 True

Typed programs are more reliable, as they offer strong static guarantees. For example, if
the program is type-checked, then we know for sure that expressions like add 1 True will
never occur during runtime. Moreover, typed programs often have better performance atruntime since a compiler can apply optimizations according to the type information.

3

1 Introduction
However, writing type annotations can be tedious, especially when the type annotations
can be inferred from the context. Consider the definition of add, which uses the built-in
primitive + : Int → Int → Int1 .
add = \x:Int. \y:Int. x + y

Here we have provided explicit type annotations for x and y. But we do not really have to:from the use of +, it is obvious that the type of these two variables are Int. What we reallywant to write is instead
add2 = \x. \y. x + y

We thus need type inference, which reconstructs missing types in expressions. In this case,with type inference, we would write add2, and type inference would automatically figure out
the right type annotations, generating add for free. Such a facility eliminates a great deal ofneedless verbosity without losing the benefits of static guarantees. Moreover, it reduces theburden of programmers, as programs are now easier to read and write.

1.1.2 The Hindley-Milner Type System
Most type inference systems used in practice are based on the Hindley-Milner (HM) typesystem [Damas and Milner 1982; Hindley 1969]. The HM system comes with a simple yet
effective algorithm that can infer the most general, or principal, types for expressions without
any type annotations.
For example, consider the expression
id = \x. x

There are many possible types we can give for id, including Int → Int, and Bool → Bool,etc. In this case, HM will derive the principal type for id: ∀a. a → a. a polymorphic typewith a universal quantifier over the type variable a. We call types without universal quantifiers, like Int → Int and Bool → Bool, monomorphic types (i.e., monotypes), and types
like ∀a. a → a polymorphic types. For this example, from the principal type ∀a. a → a,other types like Int → Int and Bool → Bool can be derived by instantiating a to Int andBool respectively. With the principal type, we can use id as in the following program:let id = \x. x
in (id 1, id True)
1

4

The syntax \ creates a lambda for defining functions. The definition is essentially equivalent to add(Intx, Int y) {return x + y;} in languages like Java.

1.1 Preliminaries

1.1.3 Higher-rank Polymorphism
While elegant and expressive, the HM system comes with a restriction: universal quantifiersin types are restricted to the top-level. For example,∀a. a → a

is a valid type, while(∀a. a → a) → int

is not as ∀ appears inside the → constructor.
This is unfortunate, as modern programming often requires higher-rank polymorphism,i.e., universal quantifiers can appear anywhere inside a type. For example, it is well-known
that rank-2 polymorphic types (i.e., universal quantifier can appear one level contravariantly
deeper in →) [Jones 1996; McCracken 1984] can be used for resource encapsulation. Thisis a well-understood technique used in Haskell’s state monad [Gill et al. 1993], which has afunction runST with the following type:runST : ∀a. (∀s. ST s a) → a

The ∀ in the rank-2 type ensures by construction that the internal state s used by the ST s a
computation is inaccessible to the rest of the program.

1.1.4 Implicit Polymorphism
System F [Girard 1986; Reynolds 1974] is the polymorphic lambda calculus with full power ofhigher-rank polymorphism, where functions like runST can be defined easily. System F has
been used extensively in research on polymorphism, and has served as the basis for various
programming language designs.
In System F, type arguments are passed explicitly. For example, consider
map :: ∀a b. (a → b) → [a] → [b]fst :: ∀a b. (a, b) → a

where map takes a function, and a list, and applies the function to every element in the list;
and fst takes out the first component from a tuple. We can use the functions as
map (Int, Char) Int (fst Int Char) [(1, 'a'), (2, 'b')]-- [(1, 2)]

However, writing type arguments, much like writing type annotations, is quite tedious. In
this case, the type arguments are almost as large as the program itself!

5

1 Introduction
For systems with polymorphism, type inference enables implicit polymorphism, where
missing type arguments are reconstructed automatically. In this case, as types can be inferred from the argument ([(1, 'a'), (2, 'b')]), with type inference we could simply
write
map fst [(1, 'a'), (2, 'b')]

There has been lots of work in extending the HM type system with implicit higher-rankpolymorphism [Dunfield and Krishnaswami 2013; Le Botlan and Rémy 2003; Leijen 2009;
Peyton Jones et al. 2007; Serrano et al. 2020, 2018].

1.1.5 Predicativity
In a system with polymorphism, one important design decision to make is whether the system is predicative or impredicative.
A system is predicative, if the type variable bound by a universal quantifier is only allowed
to be instantiated by a monotype; otherwise it is impredicative. For example, instantiating awith Int in ∀a. a → a, generating Int → Int, is predicative; while instantiating a with ∀a.
a → Int in ∀a. a → a, generating (∀a. a → Int) → (∀a. a → Int), is impredicative.

HM is an example of predicative polymorphic system, with universal quantifiers restricted tothe top-level, while System F is impredicative. It is well-known that general type inference forimpredicativity is undecidable [Wells 1999]. The most recent line of work in impredicativity
can be found in work by Serrano et al. [2020, 2018].
Type inference for a predicative type system is much easier, while still enables most of theexpressiveness of higher-rank polymorphism. Thus just like Dunfield and Krishnaswami[2013]; Peyton Jones et al. [2007], in this work, we focus on predicative implicit higher-rankpolymorphism. In the rest of this dissertation, whenever we refer to higher-rank polymorphism, unless otherwise specified, it denotes predicative implicit higher-rank polymorphism.

1.2 Contribution Overview
The goal of this dissertation is to explore the design space of type inference for implicit predicative higher-rank polymorphism, as well as to study the integration of techniques we havedeveloped into other advanced type system features including gradual typing [Siek and Taha2007] and kind inference.

61.2 Contribution Overview

1.2.1 Type Inference for Predicative Implicit Higher-rank Polymorphism
There has been much work on type inference for higher-rank polymorphism [Dunfield andKrishnaswami 2013; Odersky and Läufer 1996; Peyton Jones et al. 2007]. However, since
general type inference for higher-rank polymorphism is undecidable [Wells 1999], all work
involves difference design tradeoffs. In particular, given id:∀a. a → a, consider:(\f. (f 1, f 'a')) id

Systems including Dunfield and Krishnaswami [2013]; Odersky and Läufer [1996]; Peyton Jones et al. [2007] fail to type-check this program, as they fail to infer a polymorphic
type for f. However, much like we do not need to write type annotations in expressions like\x. \y. x + y, we should not be required to provide an explicit type annotation for f, giventhat we can derive this type information from the context: id has type ∀a. a → a, which
can serve as the type of f.
Bidirectional type checking, popularized by local type inference [Pierce and Turner 2000],
exploits the idea of recovering type information from adjacent nodes in the syntax tree. For
example, using bidirectional type checking, type information can be propagated inwards
in programs like (\x. x + 1): Int → Int. Several systems [Dunfield and Krishnaswami
2013; Peyton Jones et al. 2007] integrates bidirectional type checking into type inference forhigher-rank polymorphism.
Unfortunately, traditional bidirectional typechecking is not working for this example. Specifically, traditional bidirectional checking does not make use of the type information from theargument (in this case, id) to infer the type of the function (in this case, (\f. (f 1, f'a'))).

The first contribution of this dissertation is a design of a variant of bidirectional typechecking algorithm that, when applied to higher-rank polymorphism, is able to accept theabove example without any additional type annotations. Like other systems, the design ofthis system involves different tradeoffs, and those difference tradeoffs provide new insights
for designing bidirectional type checking algorithms. Besides illustrating the key idea, we
also compare our system in detail with other systems with (bidirectional) type inference forhigher-rank polymorphism.

1.2.2 Gradually Typed Higher-rank Polymorphism
Static typing enjoys many benefits. For example, it is guaranteed that ill-typed programs will
be rejected at compile-time. Also, types serve as good documentation for programs, as wellas to accelerate program execution when combined with type-based compiler optimization.
So far we have only considered programs with static typing.

7

1 Introduction
On the other hand, dynamic typing, where majority of its type checking is performed at
run-time, has its own merits. Languages with dynamic typing, like Python and Javascript,
are generally considered to have less cognitive load, better expressiveness, as well as bettersupport for fast prototyping.
Gradual typing [Siek and Taha 2006] is designed to enjoy the best of both worlds. Languages with gradual typing include Clojure [Bonnaire-Sergeant et al. 2016], Python [Lehtosalo et al. 2006; Vitousek et al. 2014], TypeScript [Bierman et al. 2014], etc. With gradualtyping, programmers have fine-grained control over the static-to-dynamic spectrum: programs can be partially type-checked, where the type-checked part enjoys benefits from static
typing, and the untype-checked part is dynamically type-checked. In particular, gradual typing also provides an explicit type annotation ?, which indicates unknown types that should
be type-checked during runtime. As an example, in the following program:\x:Int. \y:?. (x + 1, not y)
x is statically type-checked and y is dynamically type-checked, so that the following programis rejected at compile-time:(\x:Int. \y:?. (x + 1, not y)) 'a' False

while the following is only rejected at runtime:(\x:Int. \y:?. (x + 1, not y)) 1 'a'

However, while gradual typing is increasingly popular in the programming language research community [Tobin-Hochstadt 2019], the integration of gradual typing with advanced
type features still largely remains unclear. This is not surprising though, as great care must
be taken in the design of the interaction between static types features and the unknown type.
Therefore, there has been more work in adding basic static typing support in dynamically
typed languages, than gradualizing statically typed languages with advanced features.
The second contribution of this dissertation is the integration of gradual typing and higherrank polymorphism. Higher-rank polymorphism, as we have shown, is pervasive in languages like Haskell. Therefore, our study provides a step forward in adding gradual types inmodern static typing languages. In particular, with gradual typing, we are able to accept(\f:?. (f 1, f 'a')) id

without providing explicitly the large type annotation for f.
Designing a gradually typed higher-rank polymorphic type system poses great challenges.
First, it requires to integrate subtyping and consistency. Implicit polymorphism is often built
on a subtyping relation, which implicitly converts a more general type (e.g., ∀a. a → a) to amore specific one (e.g., Int → Int) so that for example id can be used where an expression of8

1.2 Contribution Overview
type Int → Int is expected. On the other hand, gradual typing deals with the powerful unknown type, so that an expression with the unknown type can be used as an expression of any
type. We show that existing design of such integration [Siek and Taha 2007] is inadequate,
and we provide a generalized design that is able to deal with higher-rank polymorphism. Second, we must ensure that our system is well-designed, by showing that our system satisfies
the correctness criteria [Siek et al. 2015]. We will show that the dynamic gradual guarantee isparticular tricky to deal with.

1.2.3 Type Promotion and Kind Inference for Datatypes
An ideal type inference algorithm should enjoy various desired properties: soundness, completeness and inference of principal types. An algorithm is sound and complete, if it accepts
and only accepts programs that are well-typed in the declarative type system.
However, design of type inference algorithms is challenging, as it often involves low-level
details, including constraint solving, unification, etc. In systems with advanced type features,
like higher-rank polymorphism, the inference algorithm further needs to deal with the scoping and dependency issues between different kinds of variables. For example, consider thetype ∀a. ∀b. a → b and ∀c. c → c. Intuitively, we know that the first type is more general than the other, but how can show that algorithmically? We first need to skolemize c as a
type variable, and then instantiate a, b with fresh unification variables, and finally show that
we can solve those unification variables with c. Handling the scoping and dependency issues
properly is tricky.
In the third part of the dissertation, we propose a novel type promotion process, whichhelps resolve the dependency between variables during type inference. We show that it leads
to an arguably simpler type inference algorithm for higher-rank polymorphism, and can beeasily applied to other advanced features like gradual typing.
Another advanced feature that involves more complicated scoping and dependency issues
is dependent types. So far, we have only considered programs where expressions can dependon types, e.g., the term 2 has type Int. In dependently typed languages, types can dependon expressions, e.g., the type Vec Int 2 may express a vector of integer of length 2. A vector
with polymorphic length can then be expressed as ∀n:Int. Vec Int n. Note how the term nof type Int scopes over the body of the type.
In the second half of this part, as another application of promotion, we consider type inference for dependent types in a practical setting; that is, kind inference for datatypes. Datatypedeclarations offer a way to define new types along with their constructors. For example,
data Maybe a = Nothing | Just a91 Introduction
defines a type Maybe a with two constructors, Nothing, and Just which has one field of typea. This datatype is useful to express optional types. For example, we can express a divisionalgorithm which, when the second argument is 0, returns Nothing, or otherwise wraps theresult inside Just.
div : Int → Int → Maybe Int
div 42 2 -- Just 21
div 42 0 -- Nothing

Note that Maybe takes a type (e.g., Int in this case), and returns another type (e.g., Maybe Int).
In the same sense as expressions are classified using types, types are classified using kinds. We
say that primitive types like Int have kind *, and therefore Maybe has kind * → *. We callthe process of inferring the kind of types kind inference.
In type systems with only simple types, kind inference for datatypes is straightforward.
However, in recent years, languages like Haskell have seen a dramatic surge of new features,and kind inference for datatypes has become non-trivial. For example, consider inferring
the kind of the following datatype declarations:data App f adata Fix fdata T= MkApp (f a)= In (f (Fix f))= MkT1 (App Maybe Int) |MkT2 (App Fix Maybe)

which includes several complicated features: in the definition of App, the type of f and a canbe polymorphic; in T, the type Maybe and Fix are both used in their unsaturated form (i.e.,
Maybe and Fix are not applied to any type arguments), and App is used polymorphically.

In the second half of this part, we study kind inference for datatypes in two systems:
Haskell98, and a more advanced system we call PolyKinds, based on the extensions in modern Haskell, where the type and kind languages are unified, and dependently typed. We showthat proper design of kind inference for datatypes is challenging, and unification between
dependent types also poses a threat to termination. Both formulations are novel and without precedent, and thus this work can serve as a guide to language designers who wish toformalize their datatype declarations.

1.3 Contributions
In particular, I offer the following specific contributions:
Part II• Chapter 3 presents an implicit higher-rank polymorphic type system AP, which
infers higher-rank types, generalizes the HM type system, and has polymorphic

10

1.3 Contributions
let as syntactic sugar. As far as we are aware, no previous work enables an HMstyle let construct to be expressed as syntactic sugar.
The system is defined based on a variant of bidirectional type (checking) [Pierce
and Turner 2000] with a new application mode. The new variant preserves theadvantage of bidirectional type checking, namely many redundant type annotations are removed, while certain programs can type check with even fewer
annotations than traditional bidirectional type checking algorithm. We believe
that, similarly to standard bidirectional type checking, bidirectional type checking with an application mode can be applied to a wide range of type systems.
Part III• Chapter 4 integrates implicit higher-rank polymorphism with gradual types [Siekand Taha 2006], which is, as far as we are aware, the first work on bridging the
gap between implicit higher-rank polymorphism and gradual typing.
We start by studying the gradually typed subtyping and type consistency [Siekand Taha 2006], the central concept for gradual typing, for implicit higher-rankpolymorphism. To accomplish this, we first define a framework for consistentsubtyping [Siek and Taha 2007] with
– a new definition of consistent subtyping that subsumes and generalizes thatof Siek and Taha, and can deal with polymorphism and top types. Our new
definition of consistent subtyping preserves the orthogonality between consistency and subtyping. To slightly rephrase Siek and Taha [2007], the mottoof this framework is that: Gradual typing and polymorphism are orthogonal
and can be combined in a principled fashion.2– a syntax-directed version of consistent subtyping that is sound and completewith respect to our definition of consistent subtyping. The syntax-directedversion of consistent subtyping is remarkably simple and well-behaved, anddoes not require the restriction operator of Siek and Taha [2007].
Based on consistent subtyping, we then present the design of GPC, which stands
for Gradually Polymorphic Calculus: a (source-level) gradually typed calculus
for predicative implicit higher-rank polymorphism that uses our new notion of
consistent subtyping. We prove that our calculus satisfies the static aspects of
the refined criteria for gradual typing [Siek et al. 2015], and is type-safe by a
type-directed translation to λB [Ahmed et al. 2009]. We then give a sound and2Note here that we borrow Siek and Taha’s motto mostly to talk about the static semantics. As Ahmed et al.
[2009] show there are several non-trivial interactions between polymorphism and casts at the level of the
dynamic semantics.

11

1 Introduction
complete bidirectional algorithm for implementing the declarative system basedon the design principle of Garcia and Cimini [2015].
• Chapter 5 proposes an extension of GPC with type parameters [Garcia and Cimini 2015] as a step towards restoring the dynamic gradual guarantee [Siek et al.
2015]. The extension significantly changes the algorithmic system. The newalgorithm features a novel use of existential variables with a different solution
space, which is a natural extension of the approach by Dunfield and Krishnaswami[2013].
Part IV• Chapter 6 proposes an arguably simpler algorithmic subtyping of the type inference algorithm for higher-rank implicit polymorphism, based on a new strategy called promotion in the type inference in context [Dunfield and Krishnaswami
2013; Gundry et al. 2010] framework. Promotion helps resolve the dependency
between variables during solving, and can be naturally generalized to more complicated types.
In this part, we first apply promotion to the unification algorithm for simply
typed lambda calculus, and then its polymorphic extension to the subtyping algorithm for implicit predicative higher-rank polymorphism.
• Chapter 7 applies the design of promotion in the context of kind inference fordatatypes, and presents two kind inference systems for Haskell. The first system, we believe, is the first formalization of this aspect of Haskell98, and the second one models the challenging features for kind inference in modern Haskell.
Specifically,– We formalize Haskell98’s datatype declarations, providing both a declarative
specification and syntax-driven algorithm for kind inference. We prove thatthe algorithm is sound and observe how Haskell98’s technique of defaulting
leads to incompleteness.
– We then present a type and kind language that is unified and dependently
typed, modeling the challenging features for kind inference in modern Haskell.
We include both a declarative specification and a syntax-driven algorithm.
The algorithm is proved sound, and we observe where and why completeness fails. In the design of our algorithm, we must choose between completeness and termination; we favor termination but conjecture that an alternative design would regain completeness. Unlike other dependently typed

12

1.3 Contributions
languages, we retain the ability to infer top-level kinds instead of relying on
compulsory annotations.
This thesis is largely based on the publications by the author [Xie et al. 2018, 2019a,b,c;
Xie and Oliveira 2017, 2018], as indicated below. The metatheory of those works is mostly
verified using the Coq proof assistant, including type safety, coherence, etc.
Chapter 3: Ningning Xie and Bruno C. d. S. Oliveira. 2018. “Let Arguments Go First”. In
European Symposium on Programming (ESOP)3 .
Chapter 4: Ningning Xie, Xuan Bi, and Bruno C. d. S. Oliveira. 2018. “Consistent Subtyping
for All”. In European Symposium on Programming (ESOP)4 .
Chapter 5: Ningning Xie, Xuan Bi, Bruno C. d. S. Oliveira, and Tom Schrijvers. 2019. “Consistent Subtyping for All”. In ACM Transactions on Programming Languages and Systems (TOPLAS)5 .
Chapter 6: Ningning Xie and Bruno C. d. S. Oliveira. 2017. “Towards Unification for Dependent Types” (Extended abstract), In Draft Proceedings of Trends in Functional Programming (TFP)6 .
Chapter 7: Ningning Xie, Richard Eisenberg and Bruno C. d. S. Oliveira. 2020. “Kind Inference for Datatypes”. In Symposium on Principles of Programming Languages (POPL)7 .

3

Proofs in https://bitbucket.org/ningningxie/let-arguments-go-first/src/master/.
Proofs in https://github.com/xnning/Consistent-Subtyping-for-All.
5
Proofs in https://github.com/xnning/Consistent-Subtyping-for-All.
6
Proofs in https://xnning.github.io/papers/sanitized-type-inference-in-context.pdf.
7
Proofs in https://arxiv.org/abs/1911.06153.
413

2 Background
This chapter sets the stage for type systems in later chapters. Section 2.1 reviews the HindleyMilner type system [Damas and Milner 1982; Hindley 1969; Milner 1978], a classical typesystem for the lambda calculus with parametric polymorphism. Section 2.2 presents theOdersky-Läufer type system [Odersky and Läufer 1996], which extends upon the HindleyMilner type system by putting higher-rank type annotations to work. Finally in Section 2.3
we introduce the Dunfield-Krishnaswami type system, a bidirecitonal higher-rank type system. Here we pay particular attention to the Dunfield-Krishnaswami system as it serves
as a basis for extensions in later chapters; for example, Chapter 4 is a direct extension of
Dunfield-Krishnaswami. There is plenty of other related work to higher-rank type system(e.g., Peyton Jones et al. [2007]), and we include a more substantive discussion of those works
in Chapter 8.

2.1 The Hindley-Milner Type System
The global type-inference algorithms employed in modern functional languages such as ML,
Haskell and OCaml, are derived from the Hindley-Milner type system. The Hindley-Milner
type system, hereafter referred to as HM, is a polymorphic type discipline first discovered in
Hindley [1969], later rediscovered by Milner [1978], and also closely formalized by Damas
and Milner [1982]. In what follows, we first review its declarative specification, then discuss
the property of principality, and finally talk briefly about its algorithmic system.

2.1.1 Declarative System
The declarative system of HM is given in Figure 2.1.

Syntax. The expressions e include variables x, literals n, lambda abstractions λx. e, applications e1 e2 and let x = e1 in e2 . Note here lambda abstractions have no type annotations,and the type information is to be reconstructed by the type system.

15

2 Background

Expressions
Types
Monotypes
Contexts

e
σ
τ
Ψ

::=
::=
::=
::=

x | n | λx. e | e1 e2 | let x = e1 in e2∀ai i . τ
Int | a | τ1 → τ2
• | Ψ, x : σΨ `HM e : σ(Typing)hm-var(x : σ) ∈ Ψ

hm-intΨ `HM x : σΨ `HM n : Int

hm-appΨ `HM e1 : τ1 → τ2

Ψ `HM e2 : τ1

Ψ `HM e1 e2 : τ2
hm-genai i ∈/ fv (Ψ)Ψ `HM e : τΨ `HM e : ∀ai i . τ

hm-lamΨ, x : τ1 `HM e : τ2

Ψ `HM λx. e : τ1 → τ2hm-letΨ `HM e1 : σΨ, x : σ `HM e2 : τΨ `HM let x = e1 in e2 : τ
hm-instΨ `HM e : ∀ai i . τΨ `HM e : τ [ ai 7→ τi i ]Figure 2.1: Syntax and static semantics of the Hindley-Milner type system.

Types consist of polymorphic types σ and monomorphic types (monotypes) τ . A polymorphic type is a sequence of universal quantifications (which can be empty) followed by amonotype τ , which can be the integer type Int, type variables a and function types τ1 → τ2 .
A context Ψ tracks the type information for variables. We implicitly assume items in acontext are distinct throughout the thesis.
Typing. The declarative typing judgment Ψ `HM e : σ derives the type σ of the expressione under the context Ψ. Rule hm-var fetches a polymorphic type x : σ from the context.
Literals always have the integer type (rule hm-int). For lambdas (rule hm-lam), since there
is no type given for the binder, the system guesses a monotype τ1 as the type of x, and derives
the type τ2 for the body e, returning a function τ1 → τ2 . Function types are eliminated byapplications. In rule hm-app, the type of the argument must match the parameter’s type τ1 ,and the whole application returns type τ2 .
Rule hm-let is the key rule for flexibility in HM, where a polymorphic expression can be
defined, and later instantiated with different types in the call sites. In this rule, the expression
e1 has a polymorphic type σ, and the rule adds x : σ into the context to type-check e2 .
Rule hm-gen and rule hm-inst correspond to generalization and instantiation respectively. In rule hm-gen, we can generalize over type variables ai i which are not bound in16

2.1 The Hindley-Milner Type System`HM σ1 <: σ2(Subtping)hm-s-forallRhm-s-refla ∈/ fv (σ1 )`HM τ <: τ`HM σ1 <: σ2

hm-s-forallL`HM σ1 [a 7→ τ ] <: σ2`HM σ1 <: ∀a. σ2`HM ∀a. σ1 <: σ2Figure 2.2: Declarative subtyping in the Hindley-Milner type system.

the type context Ψ. In rule hm-inst, we can instantiate the type variables with arbitrary
monotypes.

2.1.2 Principal Type Scheme
One salient feature of HM is that the system enjoys the existence of principal types, without
requiring any type annotations. Before we present the definition of principal types, let’s first
define the subtyping relation among types.
The judgment `HM σ1 <: σ2 , given in Figure 2.2, reads that σ1 is a subtype of σ2 . The
declarative subtyping relation indicates that σ1 is more general than σ2 : for any instantiationof σ2 , we can find an instantiation of σ1 to make two types match. Rule hm-s-refl is simply
reflexive for monotypes. Rule hm-s-forallR has a polymorphic type ∀a. σ2 on the righthand side. In order to prove the subtyping relation for all possible instantiations of a, weskolemize a, by making sure a does not appear in σ1 (up to α-renaming). In this case, if σ1 isstill a subtype of σ2 , we are sure then whatever a can be instantiated to, σ1 can be instantiatedto match σ2 . In rule hm-s-forallL, by contrast, the a in ∀a. σ1 can be instantiated to any
monotype to match the right hand side. Here are some examples of the subtyping relation:`HM

Int → Int <: Int → Int`HM∀a. a → a <: Int → Int

Given the subtyping relation, now we can formally state that HM enjoys principality. Thatis, for every well-typed expression in HM, there exists one type for the expression, which ismore general than any other types the expression can derive. Formally,
Theorem 2.1 (Principality for HM). If Ψ `HM e : σ, then there exists σ ′ such that Ψ `HM e :σ ′ , and for all σ ′′ such that Ψ `HM e : σ ′′ , we have `HM σ ′ <: σ ′′ .
Consider the expression λx. x. It has a principal type ∀a. a → a, which is more generalthan any other options, e.g., Int → Int, (Int → Int) → (Int → Int), etc.

17

2 Background2.1.3 Algorithmic Type System
The declarative specification of HM given in Figure 2.1 does not directly lead to an algorithm.
In particular, the system is not syntax-directed, and there are still many guesses in the system,
such as in rule hm-lam.

Syntax-directed System. A type system is syntax-directed, if the typing rules are completely driven by the syntax of expressions; in other words, there is exactly one typing rulefor each syntactic form of expressions. However, in Figure 2.1, the rule for generalization(rule hm-gen) and instantiation (rule hm-inst) can be applied anywhere.
A syntax-directed presentation of HM can be easily derived. In particular, from the typing
rules we observe that, except for fetching a variable from the context (rule hm-var), theonly place where a polymorphic type can be generated is for the let expressions (rule hmlet). Thus, a syntax-directed system of HM can be presented as the original system, withinstantiation applied to only variables, and generalization applied to only let expressions.
Specifically,hm-let-gen

Ψ `HM e1 : τ

hm-var-inst(x : ∀ai i . τ ) ∈ Ψ

ai i = fv (τ ) − fv (Ψ)Ψ `HM x : τ [ ai 7→ τi i ]Ψ, x : ∀ai i . τ `HM e2 : τΨ `HM let x = e1 in e2 : τ

A syntax-directed subtyping relation can also be derived, by restricting rule hm-s-forallL
to allow only for a monotype on the right:
hm-s-a-forallL`HM σ[a 7→ τ1 ] <: τ`HM ∀a. σ <: τType Inference. The guessing part of the system can be deterministically solved by thetechnique of type inference. There exists a sound and complete type inference algorithm for
HM [Damas and Milner 1982], which has served as the basis for the type inference algorithm
for many other systems [Odersky and Läufer 1996; Peyton Jones et al. 2007], including thesystem presented in Chapter 3. We will discuss more about it in Chapter 3.

18

2.2 The Odersky-Läufer Type System

2.2 The Odersky-Läufer Type System
The HM system is simple, flexible and powerful. Yet, since the type annotations in lambda
abstractions are always missing, HM only derives polymorphic types of rank 1. That is, universal quantifiers only appear at the top level. Polymorphic types are of higher-rank, if universal quantifiers can appear anywhere in a type.
Essentially implicit higher-rank types enable much of the expressive power of System F,with the advantage of implicit polymorphism. Complete type inference for System F isknown to be undecidable [Wells 1999]. Odersky and Läufer [1996] proposed a type system, hereafter referred to as OL, which extends HM by allowing lambda abstractions to have
explicit higher-rank types as type annotations. As a motivation, consider the following program1 :(\f. (f 1, f 'a')) (\x. x)

which is not typeable under HM because it fails to infer the type of f: f is supposed to bepolymorphic as it is applied to two arguments of different types. With OL we can add thetype annotation for f:(\f : ∀a. a → a. (f 1, f 'a')) (\x. x)

Note that the first function now has a rank-2 type, as the polymorphic type ∀a. a → a
appears in the argument position of a function:(\f : ∀a. a → a. (f 1, f 'a')) : (∀a. a → a) → (Int, Char)In the rest of this section, we first give the definition of the rank of a type, and then present
the declarative specification of OL, and show that OL is a conservative extension of HM.

2.2.1 Higher-rank Types
We define the rank of types as follows.
Definition 1 (Type rank). The rank of a type is the depth at which universal quantifiers appear contravariantly [Kfoury and Tiuryn 1992]. Formally,rank(τ )=0

rank(σ1 → σ2 )=max(rank(σ1 ) + 1, rank(σ2 ))rank(∀a. σ)=max(1, rank(σ))

Below we give some examples:1

For the purpose of illustration, we assume basic constructs like booleans and pairs in examples.

19

2 Background

Expressions
Types
Monotypes
Contexts

e
σ
τ
Ψ

::=
::=
::=
::=

x | n | λx : σ. e | λx. e | e1 e2 | let x = e1 in e2
Int | a | σ1 → σ2 | ∀a. σ
Int | a | τ1 → τ2
• | Ψ, x : σ | Ψ, aFigure 2.3: Syntax of the Odersky-Läufer type system.

rank(Int → Int)=0rank(∀a. a → a)=1

rank(Int → (∀a. a → a))=1

rank((∀a. a → a) → Int)=

2

From the definition, we can see that monotypes always have rank 0, and the polymorphic
types in HM (σ in Figure 2.1) has at most rank 1.

2.2.2 Declarative System
Syntax. The syntax of OL is given in Figure 2.3. Comparing to HM, we observe the following differences.
First, expressions e include not only unannotated lambda abstractions λx. e, but also annotated lambda abstractions λx : σ. e, where the type annotation σ can be a polymorphic
type. Thus unlike HM, the argument type for a function is not limited to a monotype.
Second, the polymorphic types σ now include the integer type Int, type variables a, functions σ1 → σ2 and universal quantifications ∀a. σ. Since the argument type in a function canbe polymorphic, we see that OL supports arbitrary rank of types. The definition of monotypes remains the same, with polymorphic types still subsuming monotypes.
Finally, in addition to variable types, the contexts Ψ now also keep track of type variables.
Note that in the original work in Odersky and Läufer [1996], the system, much like HM,
does not track type variables; instead, it explicitly checks that type variables are fresh withrespect to a context or a type when needed. The difference is more presentational rather
than semantic. Here we include type variables in contexts, as it sets us well for the DunfieldKrishnaswami type system to be introduced in the next section. Moreover, it provides acomplete view of possible formalisms of contexts in a type system with generalization.
Now since the context tracks type variables, we define the notion of well-formedness oftypes, given in Figure 2.4. For a type to be well-formedness, it must have all its free variablebound in the context. All rules are straightforward.
Type System. The typing rules for OL are given in Figure 2.5.

20

2.2 The Odersky-Läufer Type SystemΨ `OL σ(Type Well-formedness)
ol-wf-arrowol-wf-tvar

ol-wf-inta∈ΨΨ `OL IntΨ `OL σ1Ψ `OL aol-wf-forallΨ `OL σ2Ψ, a `OL σΨ `OL σ1 → σ2Ψ `OL ∀a. σ

Figure 2.4: Well-formedness of types in the Odersky-Läufer type system.

Ψ `OL e : σ(Typing)ol-varol-lamann(x : σ) ∈ Ψ

ol-intΨ `OL x : σΨ `OL n : Int

ol-lamΨ `OL τΨ, x : σ1 `OL e : σ2Ψ `OL λx : σ1 . e : σ1 → σ2
ol-appΨ, x : τ `OL e : σΨ `OL e1 : σ1 → σ2Ψ `OL λx. e : τ → σ
ol-letΨ `OL e1 : σ1Ψ `OL e2 : σ1Ψ `OL e1 e2 : σ2
ol-genΨ, x : σ1 `OL e2 : σ2Ψ, a `OL e : σΨ `OL let x = e1 in e2 : σ2
ol-subΨ `OL e : σ1Ψ `OL e : ∀a. σΨ `OL σ1 <: σ2Ψ `OL e : σ2Ψ `OL σ1 <: σ2(Subtyping)ol-s-tvar

ol-s-inta∈ΨΨ `OL a <: aΨ `OL Int <: Int

ol-s-forallLΨ `OL τΨ `OL σ[a 7→ τ ] <: σ2Ψ `OL ∀a. σ1 <: σ2ol-s-arrowΨ `OL σ3 <: σ1Ψ `OL σ2 <: σ4Ψ `OL σ1 → σ2 <: σ3 → σ4
ol-s-forallRΨ, a `OL σ1 <: σ2Ψ `OL σ1 <: ∀a. σ2Figure 2.5: Static semantics of the Odersky-Läufer type system.

21

2 Background
Rule ol-var and rule ol-int are the same as that of HM. Rule ol-lamann type-checks
annotated lambda abstractions, by simply putting x : σ into the context to type the body. For
unannotated lambda abstractions in rule ol-lam, the system still guesses a mere monotype.
That is, the system never guesses a polymorphic type for lambdas; instead, an explicit polymorphic type annotation is required. Rule ol-app and rule ol-let are similar as HM, exceptthat polymorphic types may appear in return types. In the generalization rule ol-gen, we
put a fresh type variable a into the context, and the return type σ is then generalized over a,returning ∀a. σ.
The subsumption rule ol-sub is crucial for OL, which allows an expression of type σ1 tohave type σ2 with σ1 being a subtype of σ2 (Ψ `OL σ1 <: σ2 ). Note that the instantiation
rule hm-inst in HM is a special case of rule ol-sub, as we have ∀ai i . τ <: τ [ ai 7→ τi i ] byapplying rule hm-s-forallL repeatedly.
The subtyping relation of OL Ψ `OL σ1 <: σ2 also generalizes the subtyping relationof HM. In particular, in rule ol-s-arrow, functions are contravariant on arguments, andcovariant on return types. This rule allows us to compare higher-rank polymorphic types,
rather than just polymorphic types with universal quantifiers only at the top level. For example,Ψ `OL ∀a. a → a<:

Int → IntΨ `OL Int → (∀a. a → a)<:

Int → (Int → Int)Ψ `OL (Int → Int) → Int<:(∀a. a → a) → Int

2.2.3 Relating to HM
It can be proved that OL is a conservative extension of HM. That is, every well-typed expression in HM is well-typed in OL, modulo the different representation of contexts.
Theorem 2.2 (Odersky-Läufer type system conservative over Hindley-Milner type system).
If Ψ `HM e : σ, suppose Ψ′ is Ψ extended with type variables in Ψ and σ, then Ψ′ `OL e : σ.
Moreover, since OL is predicative and only guesses monotypes for unannotated lambda
abstractions, its algorithmic system can be implemented as a direct extension of the one forHM.

2.2.4 Discussion: Variance and Eta-equality
As we have discussed before, the subtyping rule for functions in OL is contravariant in arguments, and covariant in return types (rule ol-s-arrow). This is a design choice rather

22

2.3 The Dunfield-Krishnaswami Type Systemthan a requirement. In fact, in some systems, e.g., Serrano et al. [2020, 2018], all type constructs are invariant, including functions, which makes type inference for impredicativity
much easier in their setting. This is one of the reasons that the Glasgow Haskell Compiler(GHC), the state-of-the-art Haskell compiler, has recently removed co/contravariant arrowfrom the language [Peyton Jones 2020].
Contravariance and covariance are more powerful than invariance, in the sense that they
can accept some more programs. But they also come with a cost. In particular, if we translatea higher-rank implicit polymorphic type system into a system with explicit polymorphism(e.g. System F [Girard 1986]), then contravariance and covariance often require the target
language to support η-equality (i.e., an expression λx. e x is equivalent to e). We show sucha translation in Section 3.3. Unfortunately, while η-equality is sound in System F, it may
be unsound in some other languages, like Haskell. Specifically, in Haskell, an expression oftype Int may reduce to a value of type Int, or it may actually be bottom (e.g., undefined in
Haskell), which throws a runtime error when evaluated. On the other hand, \x. undefinedx, the η-expanded form of undefined, is always a value, and so is semantically different from
undefined. Thus η-equality is unsound in Haskell.

In systems presented in this thesis, as we do not model the bottom value (or any side effects), we support the contravariant/ covariant function subtyping rule as rule ol-s-arrow,
while we see no particular challenges in supporting invariant constructs instead.

2.3 The Dunfield-Krishnaswami Type System
Both HM and OL derive only monotypes for unannotated lambda abstractions. OL improves on HM by allowing polymorphic lambda abstractions but requires the polymorphic
type annotations to be given explicitly. The Dunfield-Krishnaswami type system [Dunfield
and Krishnaswami 2013], hereafter refered to as DK, give a bidirectional account of higherrank polymorphism, where type information can be propagated through the syntax tree.
Therefore, it is possible for a variable bound in a lambda abstraction without explicit type
annotations to get a polymorphic type. In this section, we first review the idea of bidirectional type checking, and then present the declarative DK and discuss its algorithm.

2.3.1 Bidirectional Type Checking
Bidirectional type checking has been known in the folklore of type systems for a long time.
It was popularized by Pierce and Turner’s work on local type inference [Pierce and Turner
2000]. Local type inference was introduced as an alternative to HM type systems, which

23

2 Background
could easily deal with polymorphic languages with subtyping. The key idea in local type
inference is simple. The ”local” in local type inference comes from the fact that:“... missing annotations are recovered using only information from adjacent nodes
in the syntax tree, without long-distance constraints such as unification variables.”Bidirectional type checking is one component of local type inference that, aided by some
type annotations, enables type inference in an expressive language with polymorphism andsubtyping. In its basic form typing is split into inference and checking modes. The most salientfeature of a bidirectional type-checker is when information deduced from inference mode isused to guide checking of an expression in checking mode.
Since Pierce and Turner’s work, various other authors have proved the effectiveness ofbidirectional type checking in several other settings, including many different systems with
subtyping [Davies and Pfenning 2000; Dunfield and Pfenning 2004], systems with dependent
types [Asperti et al. 2012; Coquand 1996; Löh et al. 2010; Xi and Pfenning 1999], etc.
In particular, bidirectional type checking has also been combined with HM-style techniques for providing type inference in the presence of higher-rank type, including DK and
Peyton Jones et al. [2007]. Let’s revisit the example in Section 2.2:(\f. (f 1, f 'a')) (\x. x)

which is not typeable in HM as it they fail to infer the type of f. In OL, it can be type-checkedby adding a polymorphic type annotation on f. In DK, we can also add a polymorphic type
annotation on f. But with bidirectional type checking, the type annotation can be propagatedfrom somewhere else. For example, we can rewrite this program as:((\f. (f 1, f 'c')) : (∀a. a → a) → (Int, Char)) (\x . x)

Here the type of f can be easily derived from the type signature using checking mode inbidirectional type checking.
Dunfield and Pfenning [2004] establish a design principle of bidirectional type checking
inspired by mode correctness from logical programming, where introduction rules are distinguished from elimination rules. Following the design principle, constructors corresponding
to introduction rules (e.g., tuples) are checked against a given type, while destructors corresponding to elimination rules (e.g., tuple projections) infer a type. DK is designed following
this principle.

2.3.2 Declarative System
Syntax. The syntax of the DK is given in Figure 2.6. Comparing to OL, only the definition of expressions slightly differs. First, the expressions e in DK have no let expressions.

24

2.3 The Dunfield-Krishnaswami Type System

Expressions
Types
Monotypes
Contexts

e
σ
τ
Ψ

::=
::=
::=
::=

x | n | λx. e | e1 e2 | e : σ
Int | a | σ1 → σ2 | ∀a. σ
Int | a | τ1 → τ2
• | Ψ, x : σ | Ψ, aFigure 2.6: Syntax of the Dunfield-Krishnaswami Type System

Dunfield and Krishnaswami [2013] omitted let-bindings from the formal development, butargued that restoring let-bindings is easy, as long as they get no special treatment incompatible with substitution (e.g., a syntax-directed HM does polymorphic generalization onlyat let-bindings). Second, DK has annotated expressions e : σ (instead of annotated lambda
expressions λx : σ. e), in which the type annotation can be propagated into the expression,as we will see shortly.
The definitions of types and contexts are the same as in OL. Thus, DK also shares thesame well-formedness definition as in OL (Figure 2.4). We thus omit the definitions, but useΨ `DK σ to denote the corresponding judgment in DK.
Type System. Figure 2.7 presents the typing rules for DK. The system uses bidirectional
type checking to accommodate polymorphism. Traditionally, two modes are employed inbidirectional systems: the inference mode Ψ `DK e ⇒ σ, which takes a term e and producesa type σ, similar to the judgment Ψ `HM e : σ or Ψ `OL e : σ in previous systems; thechecking mode Ψ `DK e ⇐ σ, which takes a term e and a type σ as input, and ensures thatthe term e checks against σ. We first discuss rules in the inference mode.
Type Inference. Rule dk-inf-var and rule dk-inf-int are straightforward. To infer unannotated lambdas, rule dk-inf-lam guesses a monotype. For an application e1 e2 , rule dkinf-app first infers the type σ of the expression e1 . The application judgment (discussed
shortly) then takes the type σ and the argument e2 , and returns the final result type σ2 . For
an annotated expression e : σ, rule dk-inf-anno simply checks e against σ. Both rules(rule dk-inf-app and rule dk-inf-anno) have mode switched from inference to checking.
Type Checking.

Now we turn to the checking mode. When an expression is checkedagainst a type, the expression is expected to have that type. More importantly, the checking
mode allows us to push the type information into the expressions.
Rule dk-chk-int checks literals against the integer type Int. Rule dk-chk-lam is where
the system benefits from bidirectional type checking: the type information gets pushed inside an lambda. For an unannotated lambda abstraction λx. e, recall that in the inference25

2 BackgroundΨ `DK e ⇒ σ(Type Inference)

dk-inf-var

dk-inf-lam(x : σ) ∈ Ψ

dk-inf-int

Ψ `DK x ⇒ σΨ `DK n ⇒ Int

dk-inf-app

Ψ `DK e1 ⇒ σ

Ψ `DK τ1 → τ2

Ψ, x : τ1 `DK e ⇒ τ2

Ψ `DK λx. e ⇒ τ1 → τ2

Ψ `DK σ · e2 ⇒⇒ σ2Ψ `DK e1 e2 ⇒ σ2

dk-inf-annoΨ `DK e ⇐ σΨ `DK e : σ ⇒ σΨ `DK e ⇐ σ(Type Checking)dk-chk-int

dk-chk-lam

dk-chk-gen

Ψ `DK λx. e ⇐ σ1 → σ2

Ψ `DK e ⇐ ∀a. σ

Ψ, x : σ1 `DK e ⇐ σ2Ψ `DK n ⇐ Int

dk-chk-subΨ `DK e ⇒ σ1

Ψ, a `DK e ⇐ σΨ `DK σ1 <: σ2Ψ `DK e ⇐ σ2Ψ `DK σ1 · e ⇒⇒ σ2
dk-app-forallΨ `DK τΨ `DK σ[a 7→ τ ] · e ⇒⇒ σ1 → σ2

Ψ `DK ∀a. σ · e ⇒⇒ σ1 → σ2(Application judgment)
dk-app-arr

Ψ `DK e ⇐ σ1

Ψ `DK σ1 → σ2 · e ⇒⇒ σ2Figure 2.7: Static semantics of the Dunfield-Krishnaswami type system.

26

2.3 The Dunfield-Krishnaswami Type Systemmode, we can only guess a monotype for x. With the checking mode, when λx. e is checkedagainst σ1 → σ2 , we do not need to guess any type. Instead, x gets directly the (possibly
polymorphic) argument type σ1 . Then the rule proceeds by checking e with σ2 , allowing thetype information to be pushed further inside. Note how rule dk-chk-lam improves over
HM and OL, by allowing lambda abstractions to have a polymorphic argument type without
requiring type annotations.
Rule dk-chk-gen deals with a polymorphic type ∀a. σ, by putting the (fresh) type variablea into the context to check e against σ. Rule dk-chk-sub switches the mode from checking
to inference: an expression e can be checked against σ2 , if e infers the type σ1 and σ1 is asubtype of σ2 .
Application judgment. Notably, unlike HM or OL, DK does not feature an explicit instantiation rule. Instead, rule dk-inf-var directly returns a (possibly) polymorphic type,
and thus when typing applications (rule dk-app-inf), we need to explicitly discuss the possible shape of the function type.
The application judgment Ψ `DK σ1 · e ⇒⇒ σ2 is interpreted as, when we apply an expression of type σ1 to the expression e, we get a return type σ2 . For a polymorphic type(rule dk-app-forall), we instantiate the universal quantifier with a monotype, until thetype becomes a function type (rule dk-app-arr). In the function type case, since the function expects an argument of type σ1 , the rule proceeds by checking e2 against σ1 .
In some other type systems [Garcia and Cimini 2015; Xie et al. 2018, 2019a], the application judgment is replaced by matching. Using matching, rule dk-inf-app is replaced byrule dk-inf-app2.
dk-inf-app2

Ψ `DK e1 ⇒ σ
Ψ `DK σ . σ1 → σ2

Ψ `DK e2 ⇐ σ1

Ψ `DK e1 e2 ⇒ σ2
In rule dk-inf-app2, we first derive that e1 has type σ. But e1 must have a function type sothat it can be applied to an argument. We thus use the matching judgment to instantiate σinto a function σ1 → σ2 , and proceed by checking e2 against σ1 , and return the final result
σ2 . The definition of matching is given below.
Ψ `DK σ1 . σ2

(Matching)dk-m-forallΨ `DK τΨ `DK σ[a 7→ τ ] . σ1 → σ2
Ψ `DK ∀a. σ . σ1 → σ2

dk-m-arrΨ `DK σ1 → σ2 . σ1 → σ227

2 Background
Matching has two straightforward rules: rule dk-m-forall instantiates a polymorphic
type, by substituting a with a well-formed monotype τ , and continues matching on σ[a 7→ τ ];rule dk-m-arr returns the function type directly.
It can be easily shown that the presentation of rule dk-inf-app with the application judgment is equivalent to that of rule dk-inf-app2 with matching. Essentially, they both makesure that the expression being applied has an arrow type σ1 → σ2 , and then check the argument against σ1 . We sometimes use the presentation of rule dk-inf-app2 with matching,as matching is a simple and independent process whose purpose is clear. In contrast, it isrelatively less comprehensible with rule dk-inf-app and the application judgment, where all
three forms of the judgment (inference, checking, application) are mutually dependent.
Subtyping. DK shares the same subtyping relation as of OL. We thus omit the definition
and use Ψ `DK σ1 <: σ2 to denote the subtyping relation in DK.

2.3.3 Algorithmic Type System
Dunfield and Krishnaswami [2013] also presented a sound and complete bidirectional algorithmic type system. The key idea of the algorithm is using ordered algorithmic contexts forstoring existential variables and their solutions. Comparing to the algorithm for HM, theyargued that their algorithm is remarkably simple. The algorithm is later discussed and usedin Part III and Part IV. We will discuss more about it there.

2.3.4 Discussion: lazy and eager instantiation
We say that DK’s style of instantiation is lazy, where top-level quantifiers are only instantiated when needed (e.g., when applied as a function to arguments); while HM’s style of instantiation is eager, where in the syntax-directed rules, instantiation eagerly instantiates all
top-level universal quantifiers when possible (as in rule hm-var-inst). Eager instantiationis also used in the higher-rank polymorphic type system by Peyton Jones et al. [2007].
The differences between lazy and eager instantiation have pervasive consequences. The
first and direct consequence is that for the same expression lazy instantiation can derive amore polymorphic type. Consider that given id: ∀a. a → a, we want to infer the type of
id. With lazy instantiation, the algorithm can directly return ∀a. a → a unchanged, while

eager instantiation can only return a → a (among others). In this case, we can make two
types match by generalizing the final result from eager instantiation, which gives us ∀a.
a → a. However, when the system features higher-rank types, after generalization eager

instantiation may still fail to derive the same type. For example, consider the expression

28

2.3 The Dunfield-Krishnaswami Type System\x: Int. id

In this case, lazy instantion returns the type Int → ∀a. a → a, while eager instantiation
returns the type Int → a → a which, after generalization, becomes ∀a. Int → a → a,and is less polymorphic than Int → ∀a. a → a, according to the subtyping relation in OL.
In this sense naive eager instantiation may reject programs that lazy instantiation can accept,e.g.,
let g = \x:Int. id in (\f: Int → ∀a. a → a. f) g

The problem can be avoided by, for example, featuring deep skolemisation as in Peyton Joneset al. [2007]. Deep skolemisation floats out all its universal quantifiers that appear to theright of a top level arrow, so that ∀a. Int → a → a and Int → ∀a. a → a are actuallyisomorphic.
Secondly, with lazy instantiation, as instantiation is done only when necessary, we must beaware of when it is necessary. We have seen one particular case in the formalism, i.e., when
applying a top-level polymorphic type as a function to arguments (rule dk-app-forall).
When we extend the system with more constructs, there may be more cases where instantiation is needed. For example, consider we extend the system with if expressions:
if flag then id else \x. x + 1

We expect the two branches in if to return the same type. Therefore when collecting the result type from branches, we must instantiate the type of id to Int → Int, so that it matches
the type of another branch. Peyton Jones et al. [2007] discuss three possible typing formalizations of if expressions under eager instantiation. To adopt either formalization in lazy
instantiation systems, instantiation of the return types of the branches must be performed.
Finally, the choice can have unexpected interaction with other rules involving generalization, for example, let expressions. Specifically, under let generalization and lazy instantiation, typability may not be preserved after let-binding inlining. To illustrate the issue,
consider the expression:
let f = \x. x
in let g = \y. f
in e

If we generalize the type of f as in HM, then we get f:∀a. a → a. Now we continue typechecking g, which requires another step of generalization. With lazy instantiation, we getg: ∀b. b → ∀a. a → a, which is again, more polymorphic than the type g: ∀b a. b →a → a we get from eager instantiation. However, if we inline the definition of f, then the

program becomes29

2 Backgroundin let g = \y. \x. x
in e

Now both lazy and eager instantiation can only get the type g: ∀b a. b → a → a. Namely,
with lazy instantiation, let-binding inlining gets us a less polymorphic type (according to thesubtyping rule in OL). The problem has also been discussed in DK [Dunfield and Krishnaswami 2013], where they fix the issue by not treating let expressions specially – namely,
there is no generalization for let expressions at all.
In this thesis, the choice does not matter so much for the key contributions of the work.
As we do not feature deep skolemisation, we mostly follow the idea of lazy instantiation asin DK. Nevertheless, we believe that deep skolemisation is compatible with our subtyping
relations used in later chapters.

30

Part II
Bidirectional Type Checking With theApplication Mode31

3 Higher-Rank Polymorphism with theApplication Mode
We have seen in Section 2.3 that bidirectional type checking is a useful and versatile tool for
type checking and type inference. In traditional bidirectional type-checking, type information flows from functions to arguments (e.g., rule dk-in-app in Section 2.3.2). In this section,
we present a novel variant of bidirectional type checking where the type information flows
from arguments to functions. This variant retains the inference mode, but adds a so-called
application mode. Such design can remove annotations that basic bidirectional type checking cannot, and is useful when type information from arguments is required to type-checkthe functions being applied.
We illustrate our novel design of bidirectional type-checking using System AP, a lambda
calculus with implicit higher-rank polymorphism. This section first presents the declarative,
syntax-directed type system of System AP in Section 3.2. The interesting aspects about the
new type system are: 1) the typing rules, which employ a combination of the inference modeand the application mode; 2) the novel subtyping relation under an application context. Later,
we prove our type system is type-safe by a type-directed translation to System F in Section 3.3.
An algorithmic type system is discussed in Section 3.4.

3.1 Introduction and Motivation
3.1.1 Revisiting Bidirectional Type Checking
Traditional type checking rules can be heavyweight on annotations, in the sense that lambdabound variables always need explicit annotations. As we have seen in Section 2.3, bidirectional type checking provides an alternative, which allows types to propagate downward thesyntax tree. For example, in the expression (λf : Int → Int. f) (λy. y), the type of y is provided
by the type annotation on f. This is supported by the bidirectional typing rule dk-inf-app
for applications:33

3 Higher-Rank Polymorphism with the Application Mode

dk-inf-app

Ψ `DK e1 ⇒ σ

Ψ `DK σ · e2 ⇒⇒ σ2Ψ `DK e1 e2 ⇒ σ2Specifically, if we know that the type of e1 is a function from σ1 → σ2 , we can check that e2
has type σ1 . Notice that here the type information flows from functions to arguments.
One guideline for designing bidirectional type checking rules [Dunfield and Pfenning2004] is to distinguish introduction rules from elimination rules. Constructs which correspond to introduction forms are checked against a given type, while constructs corresponding
to elimination forms infer (or synthesize) their types. For instance, under this design principle, the introduction rule for literals is supposed to be in the checking mode, as in the rule
rule dk-chk-int:
dk-chk-int

Ψ `DK n ⇐ Int
Unfortunately, this means that the trivial program 1 cannot type-check, which in this casehas to be rewritten to 1 : Int.
In this particular case, bidirectional type checking goes against its original intention ofremoving burden from programmers, since a seemingly unnecessary annotation is needed.
Therefore, in practice, bidirectional type systems do not strictly follow the guideline, andusually have additional inference rules for the introduction form of constructs. For literals,
the corresponding rule is rule dk-inf-int.
dk-inf-int

Ψ `DK n ⇒ Int
Now we can type check 1, but the price to pay is that two typing rules for literals are needed.
Worse still, the same criticism applies to other constructs, for example, pairs. Below we givethe type inference and checking rules for pairs.
check-pair

inf-pairΨ ` e1 ⇒ σ1Ψ ` e2 ⇒ σ 2

Ψ ` (e1 , e2 ) ⇒ (σ1 , σ2 )Ψ ` e1 ⇐ σ 1

Ψ ` e2 ⇐ σ 2

Ψ ` (e1 , e2 ) ⇐ (σ1 , σ2 )This shows one drawback of bidirectional type checking: often to minimize annotations,many rules are duplicated for having both the inference mode and the checking mode, whichscales up with the typing rules in a type system. It is possible, though, to parameterize the34

3.1 Introduction and Motivation
rules over the direction, as is done in Peyton Jones et al. [2007], which is helpful to eliminate
the notational duplication but perhaps not the cognitive one.

3.1.2 Type Checking with The Application Mode
We propose a variant of bidirectional type checking with a new application mode (unrelated
to the application judgment in DK). The application mode preserves the advantage of bidirectional type checking, namely many redundant annotations are removed, while certain
programs can type check with even fewer annotations. Also, with our proposal, the inference mode is a special case of the application mode, so it does not produce duplications ofrules in the type system. Additionally, the checking mode can still be easily combined into
the system. The essential idea of the application mode is to enable the type information
flow in applications to propagate from arguments to functions (instead of from functions toarguments as in traditional bidirectional type checking).
To motivate the design of bidirectional type checking with an application mode, consider
the simple expression(\x. x) 1

This expression cannot type check in a traditional bidirectional type system if the systemfollows strictly the design guideline for bidirectional type checking [Dunfield and Pfenning2004], because unannotated abstractions, as a construct which correspond to introduction
forms, only have a checking mode, so annotations are required 1 . For example, ((\x. x): Int → Int) 1.

In this example we can observe that if the type of the argument is accounted for in inferring
the type of \x. x, then it is actually possible to deduce that the lambda expression has type
Int → Int, from the argument 1.

The Application Mode. If types flow from the arguments to the function, an alternative
idea is to push the type of the arguments into the typing of the function, as follows,AppΨ ` e2 ⇒ σ1Ψ; Σ, σ1 ` e1 ⇒ σ → σ2Ψ; Σ ` e1 e2 ⇒ σ2
In this rule, there are two kinds of judgments. The first judgment is just the usual inference mode, which is used to infer the type of the argument e2 . The second judgment, the1

It type-checks in DK, because in DK rules for lambdas are duplicated for having both the inference (integrated
with type inference techniques) and the checking mode.

35

3 Higher-Rank Polymorphism with the Application Modeapplication mode, is similar to the inference mode, but it has an additional context Σ. The
context Σ is a stack that tracks the types of the arguments of outer applications. In the rule
for application, the type of the argument e2 synthesizes its type σ1 , which then is pushed
into the application context Σ for inferring the type of e1 . Applications are themselves in theapplication mode, since they can be in the context of an outer application.
Lambda expressions can now make use of the application context, leading to the following
rule:
Lam

Ψ, x : σ; Σ ` e ⇒ σ2Ψ; Σ, σ ` λx. e ⇒ σ → σ2
The type σ that appears last in the application context serves as the type for x, and type checking continues with a smaller application context and x : σ in the typing context. Therefore,
using the rule rule App and rule Lam, the expression (λx. x) 1 can type-check without annotations, since the type Int of the argument 1 is used as the type of the binding x.
Note that, since the examples so far are based on simple types, obviously they can be solved
by integrating type inference and relying on techniques like unification or constraint solving(as in DK) in the algorithm. However, here the point is that the application mode helps toreduce the number of annotations without requiring such sophisticated techniques. Also, theapplication mode helps with situations where those techniques cannot be easily applied, suchas type systems with subtyping.

Interpretation of the Application Mode. As we have seen, the guideline for designing bidirectional type checking [Dunfield and Pfenning 2004], based on introduction andelimination rules, is often not enough in practice. This leads to extra introduction rules in
the inference mode. The application mode does not distinguish between introduction rules
and elimination rules. Instead, to decide whether a rule should be in the inference or theapplication mode, we need to think whether the expression can be applied or not. Variables,
lambda expressions and applications are all examples of expressions that can be applied, andthey should have application mode rules. However literals or pairs cannot be applied andshould have inference rules. For example, type checking pairs would simply have the inference mode. Nevertheless elimination rules of pairs could have non-empty application contexts (see Section 3.5.2 for details). In the application mode, arguments are always inferredfirst in applications and propagated through application contexts. An empty application con-36

3.1 Introduction and Motivationtext means that an expression is not being applied to anything, which allows us to model theinference mode as a particular case2 .
Partial Type Checking.

The inference mode synthesizes the type of an expression, andthe checking mode checks an expression against some type. A natural question is how dothese modes compare to the application mode. An answer is that, in some sense: the application mode is stronger than the inference mode, but weaker than the checking mode.
Specifically, the inference mode means that we know nothing about the type of an expression before hand. The checking mode means that the whole type of the expression is alreadyknown before hand. With the application mode we know some partial type information
about the type of an expression: we know some of its argument types (since it must be afunction type when the application context is non-empty), but not the return type.
Instead of nothing or all, this partialness gives us a finer grain notion on how much weknow about the type of an expression. For example, assume e : σ1 → σ2 → σ3 . In theinference mode, we only have e. In the checking mode, we have both e and σ1 → σ2 → σ3 .
In the application mode, we have e, and maybe an empty context (which degenerates into
the inference mode), or an application context σ1 (we know the type of first argument), oran application context σ1 , σ2 (we know the type of both arguments). Partial type checking
has also been used in techniques like colored local type inference [Odersky et al. 2001] andboxy types [Vytiniotis et al. 2006].
Trade-offs. Note that the application mode is not conservative over traditional bidirectional type checking due to the different information flow. However, it provides a new design
choice for type inference/checking algorithms, especially for those where the information
about arguments is useful. Therefore we next discuss some benefits of the application mode
for two interesting cases where functions are either variables; or lambda (or type) abstractions.

3.1.3 Benefits of Information Flowing from Arguments to Functions
Local Constraint Solver for Function Variables. Many type systems, including
type systems with implicit polymorphism and/or static overloading, need information about
the types of the arguments when type checking function variables. For example, in conventional functional languages with implicit polymorphism, function calls such as (id 1) where
2

Although the application mode generalizes the inference mode, we refer to them as two different modes. Thus
the variant of bidirectional type checking in this work is interpreted as a type system with both inference andapplication modes.

37

3 Higher-Rank Polymorphism with the Application Mode
id : ∀a. (a → a), are pervasive. In such a function call the type system must instantiate
a to Int. Dealing with such implicit instantiation gets trickier in systems with higher-rank
types. For example, Peyton Jones et al. [2007] require additional syntactic forms and relations, whereas DK adds a special-purpose application judgment.
With the application mode, all the type information about the arguments being appliedis available in the application context and can be used to solve instantiation constraints. To
exploit such information, the type system employs a special subtyping judgment called application subtyping, with the form Σ ` σ1 <: σ2 . Unlike conventional subtyping, computationally Σ and σ1 are interpreted as inputs and σ2 as output. In the example above, wehave that Int ` ∀a. a → a <: σ and we can determine that a = Int and σ = Int → Int.
In this way, the type system is able to solve the constraints locally according to the application context since we no longer need to propagate the instantiation constraints to the typing
process.
Declaration Desugaring for Lambda Abstractions. An interesting consequence of
the usage of an application mode is that it enables the following let sugar:
let x = e1 in e2 ⇝ (λx. e2 ) e1
Such syntactic sugar for let is, of course, standard. However, in the context of implementations of typed languages it normally requires extra type annotations or a more sophisticatedtype-directed translation. Type checking (λx. e2 ) e1 would normally require annotations (forexample a higher-rank type annotation for x as in OL and DK), or otherwise such annotation
should be inferred first. Nevertheless, with the application mode no extra annotations/inference is required, since from the type of the argument e1 it is possible to deduce the typeof x. Generally speaking, with the application mode annotations are never needed for applied lambdas. Thus let can be the usual sugar from the untyped lambda calculus, including
HM-style let expression and even type declarations.

3.1.4 Type Inference of Higher-rank Types
We believe the application mode can be integrated into many traditional bidirectional typesystems. In this chapter, we focus on integrating the application mode into a bidirectional
type system with higher-rank types. Our paper [Xie and Oliveira 2018] includes anotherapplication to System F.
Consider again the motivation example used in Section 2.2:(\f. (f 1, f 'a')) (\x. x)

38

3.1 Introduction and Motivation
which is not typeable in HM, but can be rewritten to include type annotations in OL and DK.
For example, both in OL and DK we can write:(\f:(∀a. a → a). (f 1, f 'c')) (\x. x)

However, although some redundant annotations are removed by bidirectional type checking, the burden of inferring higher-rank types is still carried by programmers: they are forced
to add polymorphic annotations to help with the type derivation of higher-rank types. For
the above example, the type annotation is still provided by programmers, even though thenecessary type information can be derived intuitively without any annotations: f is appliedto \x. x, which is of type ∀a. a → a.
Type Inference for Higher-rank Types with the Application Mode. Using ourbidirectional type system with an application mode, the original expression can type checkwithout annotations or rewrites: (\f. (f 1, f 'c')) (\x. x).
This result comes naturally if we allow type information flow from arguments to functions.
For inferring polymorphic types for arguments, we use generalization. In the above example,
we first infer the type ∀a. a → a for the argument, then pass the type to the function. A niceconsequence of such an approach is that, as mentioned before, HM-style polymorphic let
expressions are simply regarded as syntactic sugar to a combination of lambda/application:
let x = e1 in e2 ⇝ (λx. e2 ) e1
Conservativity over the Hindley-Milner Type System. Our type system is a conservative extension over HM, in the sense that every program that can type-check in HM is
accepted in our type system. This result is not surprising: after desugaring let into a lambda
and an application, programs remain typeable.
Comparing Predicative Higher-rank Type Inference Systems. We will give a full
discussion and comparison of related work in Section 8. Among those works, we believe DK
and the work by Peyton Jones et al. [2007] are the most closely related work to our system.
Both their systems and ours are based on a predicative type system: universal quantifiers canonly be instantiated by monotypes. So we would like to emphasize our system’s properties inrelation to those works. In particular, here we discuss two interesting differences, and also
briefly (and informally) discuss how the works compare in terms of expressiveness.
1) Inference of higher-rank types. In both works, every polymorphic type inferred by
the system must correspond to one annotation provided by the programmer. However, in

39

3 Higher-Rank Polymorphism with the Application Mode
our system, some higher-rank types can be inferred from the expression itself without any
annotation. The motivating expression above provides an example of this.
2) Where are annotations needed? Since type annotations are useful for inferring higher
rank types, a clear answer to the question where annotations are needed is necessary so that
programmers know when they are required to write annotations. To this question, previous
systems give a concrete answer: only on the bindings of polymorphic types. Our answer
is slightly different: only on the bindings of polymorphic types in abstractions that are notapplied to arguments. Roughly speaking this means that our system ends up with fewer orsmaller annotations.
3) Expressiveness. Based on these two answers, it may seem that our system should accept all expressions that are typeable in their systems. However, this is not true because theapplication mode is not conservative over traditional bidirectional type checking. Consider
the expression:(\f : (∀a. a → a) → (nat, char). f) (\g. (g 1, g 'a'))which is typeable in their system. In this case, even if g is a polymorphic binding withouta type annotation the expression can still type-check. This is because the original application rule propagates the information from the outer binding into the inner expressions. Notethat the fact that such expression type-checks does not contradict their guideline of providing type annotations for every polymorphic binder. Programmers that strictly follow their
guideline can still add a polymorphic type annotation for g. However it does mean that it
is a little harder to understand where annotations for polymorphic binders can be omitted
in their systems. This requires understanding how the applications in the checking mode
operate.
In our system the above expression is not typeable, as a consequence of the information
flow in the application mode. However, following our guideline for annotations leads to a
program that can be type-checked with a smaller annotation:(\f. f) (\g : (∀a. a → a). (g 1, g 'a')).

This means that our work is not conservative over their work, which is due to the design
choice of the application typing rule. Nevertheless, we can always rewrite programs using
our guideline, which often leads to fewer/smaller annotations.

3.2 Declarative System
This section presents the declarative, syntax-directed specification of AP. As mentioned before, the interesting aspects about the new type system are: 1) the typing rules, which employ

40

3.2 Declarative System

Expressions
Types
Monotypes
Contexts
Application Contexts

e
σ
τ
Ψ
Σ

::=
::=
::=
::=
::=

x | n | λx : σ. e | λx. e | e1 e2
Int | a | σ1 → σ2 | ∀a. σ
Int | a | τ1 → τ2
• | Ψ, x : σ• | Σ, σFigure 3.1: Syntax of System AP.

a combination of inference and application modes; 2) the novel subtyping relation under anapplication context.

3.2.1 Syntax
The syntax of the language is given in Figure 3.1.
Expressions. The definition of expressions e include variables (x), integers (n), annotatedlambda abstractions (λx : σ. e), lambda abstractions (λx. e), and applications (e1 e2 ). Notably, the syntax does not include a let expression (let x = e1 in e2 ). Let expressions can be
regarded as the standard syntax sugar (λx. e2 ) e1 , as illustrated in more detail later.
Types. Types include the integer type Int, type variables (a), functions (σ1 → σ2 ) andpolymorphic types (∀a. σ). Monotypes are types without universal quantifiers.
Contexts. Typing contexts Ψ are standard: they map a term variable x to its type σ. Again,
we implicitly assume that all variables in Ψ are distinct. In this system, the context is modeled
as the HM-style context (Section 2.1), which does not contain type variables; so the typesystem needs to explicitly ensure freshness of type variables.
The key novelty lies in the application contexts Σ, which are the main data structure needed
to allow types to flow from arguments to functions. Application contexts are modeled asa stack. The stack collects the types of arguments in applications. The context is a stackbecause if a type is pushed last then it will be popped first. For example, inferring expressione under application context (a, Int), means e is now being applied to two arguments e1 , e2 ,with e1 : Int, e2 : a, so e should be of type Int → a → σ for some σ.

3.2.2 Type System
The top part of Figure 3.2 gives the typing rules for our language. The judgment Ψ; Σ `APe ⇒ σ is read as: under typing context Ψ, and application context Σ, e has type σ. The

41

3 Higher-Rank Polymorphism with the Application ModeΨ `AP e ⇒ σ(Typing Inference)

ap-inf-int

ap-inf-lam

ap-inf-lamannΨ `AP λx. e ⇒ τ → σΨ `AP λx : σ1 . e ⇒ σ1 → σ2

Ψ, x : τ `AP e ⇒ σΨ `AP n ⇒ Int

Ψ, x : σ1 `AP e ⇒ σ2Ψ; Σ `AP e ⇒ σ
ap-app-var(x : σ1 ) ∈ Ψ(Typing Application Mode)ap-app-lamΣ `AP σ1 <: σ2Ψ, x : σ1 ; Σ `AP e ⇒ σ2Ψ; Σ `AP x ⇒ σ2Ψ; Σ, σ1 `AP λx. e ⇒ σ1 → σ2

ap-app-lamann`AP σ2 <: σ1

Ψ, x : σ1 `AP e ⇒ σ3Ψ; Σ, σ2 `AP λx : σ1 . e ⇒ σ2 → σ3
ap-app-app

ai i = fv (σ1 ) − fv (Ψ)Ψ `AP e2 ⇒ σ1iΨ; Σ, σ2 `AP e1 ⇒ σ2 → σ3σ2 = ∀ai . σ1Ψ; Σ `AP e1 e2 ⇒ σ3`AP σ1 <: σ2(Subtyping)ap-s-tvar

ap-s-int`AP a <: a`AP Int <: Int

ap-s-forallL`AP σ1 [a 7→ τ ] <: σ2

ap-s-arrow`AP σ3 <: σ1`AP σ1 → σ2 <: σ3 → σ4
ap-s-forallR/ fv (σ1 )a ∈`AP ∀a. σ1 <: σ2`AP σ1 <: σ2`AP σ1 <: ∀a. σ2Σ `AP σ1 <: σ2
ap-as-empty• `AP σ <: σ(Application Subtyping)
ap-as-forall

Σ, σ3 `AP σ1 [a 7→ τ ] <: σ2
Σ, σ3 `AP ∀a. σ1 <: σ2

ap-as-arrow`AP σ3 <: σ1Σ `AP σ2 <: σ4

Σ, σ3 `AP σ1 → σ2 <: σ3 → σ4

Figure 3.2: Typing rules of System AP.

42`AP σ2 <: σ43.2 Declarative Systemstandard inference mode Ψ `AP e ⇒ σ can be regarded as a special case when the application
context is empty. Note that the variable names are assumed to be fresh enough when newvariables are added into the typing context, or when generating new type variables.
We discuss the rules when the application context is empty first. Those rules are unsurprising. Rule ap-inf-int shows that integer literals are only inferred to have type Int underan empty application context. This is obvious since an integer cannot accept any arguments.
Rule ap-inf-lam deals with lambda abstractions when the application context is empty. In
this situation, a monotype τ is guessed for the argument, just like in previous calculi. Rule apinf-lamann also works as expected: a new variable x is put with its type σ into the typing
context, and inference continues on the abstraction body.
Now we turn to the cases when the application context is not empty. Rule ap-app-var
says that if x : σ1 is in the typing context, and σ1 is a subtype of σ2 under application context
Σ, then x has type σ2 . It depends on the subtyping rules that are explained in Section 3.2.3.
Rule ap-app-lam shows the strength of application contexts. It states that, without annotations, if the application context is non-empty, a type can be popped from the application
context to serve as the type for x. Inference of the body then continues with the rest of theapplication context. This is possible, because the expression λx. e is being applied to an argument of type σ1 , which is the type at the top of the application context stack.
For lambda abstraction with annotations λx : σ1 . e, if the application context has typeσ2 , then rule ap-app-lamann first checks that σ2 is a subtype of σ1 before putting x : σ1
in the typing context. However, note that it is always possible to remove annotations in an
abstraction if it has been applied to some arguments.
Rule ap-app-app pushes types into the application context. The application rule first infers
the type of the argument e2 with type σ1 . Then the type σ1 is generalized in the same way as
the HM type system. The resulting generalized type is σ2 . Thus the type of e1 is now inferredunder an application context extended with type σ2 . The generalization step is important toinfer higher-rank types: since σ2 is a possibly polymorphic type, which is the argument typeof e1 , then e1 is of possibly a higher-rank type.
Let Expressions. The language does not have built-in let expressions, but instead supports
let as syntactic sugar. Recall the syntactic-directed typing rule (rule hm-let-gen) for let
expressions with generalization in the HM system. With some slight reformating to match
AP, we get (without the gray-shaded parts):Ψ ` e1 ⇒ σ 1

ai i = fv (τ ) − fv (Ψ)σ2 = ∀ai i . σ1

Ψ, x : σ2 ; Σ ` e2 ⇒ σ3Ψ; Σ ` let x = e1 in e2 ⇒ σ343

3 Higher-Rank Polymorphism with the Application Mode
where we do generalization on the type of e1 , which is then assigned as the type of x while
inferring e2 . Adapting this rule to our system with application contexts would result in thegray-shaded parts, where the application context is only used for e2 , because e2 is the expression being applied. If we desugar the let expression (let x = e1 in e2 ) to (λx. e2 ) e1 , we havethe following derivation:Ψ ` e1 ⇒ σ 1

ai i = fv (σ1 ) − fv (Ψ)σ2 = ∀ai i . σ1

Ψ, x : σ2 ; Σ ` e2 ⇒ σ3Ψ; Σ, σ2 ` λx. e2 ⇒ σ2 → σ3

Ψ; Σ ` (λx. e2 ) e2 ⇒ σ3
The type σ2 is now pushed into application context in rule ap-app-app, and then assigned
to x in rule ap-app-lam. Comparing this with the typing derivations for let expressions, we
now have the same preconditions. Thus we can see that the rules in Figure 3.2 are sufficient
to express an HM-style polymorphic let construct.
Metatheory. The type system enjoys several interesting properties, especially lemmas
about application contexts. Before we present those lemmas, we need a helper definitionof what it means to use arrows on application contexts.
Definition 2 (Σ → σ). If Σ = σ1 , σ2 , ...σn , then Σ → σ means the function type σn →... → σ2 → σ1 → σ.
Such definition is useful to reason about the typing result with application contexts. One
specific property is that the application context determines the form of the typing result.
Lemma 3.1 (Σ Coincides with Typing Results). If Ψ; Σ `AP e ⇒ σ, then for some σ ′ , wehave σ = Σ → σ ′ .
Having this lemma, we can always use the judgment Ψ; Σ `AP e ⇒ Σ → σ ′ instead of
Ψ; Σ `AP e ⇒ σ.
In traditional bidirectional type checking, we often have one rule that transfers between
the inference and the checking mode, which states that if an expression can be inferred tosome type, then it can be checked with this type (e.g., rule dk-chk-sub in DK). In our system,
we regard the normal inference mode Ψ `AP e ⇒ σ as a special case, when the application
context is empty. We can also turn from the normal inference mode into the application
mode with an application context.
Lemma 3.2 (Ψ `AP ⇒ to Ψ; Σ `AP ⇒). If Ψ `AP e ⇒ Σ → σ, then Ψ; Σ `AP e ⇒ Σ → σ.
This lemma is actually a special case for the following one:44

3.2 Declarative System
Lemma 3.3 (Generalized Ψ `AP ⇒ to Ψ; Σ `AP ⇒). If Ψ; Σ1 `AP e ⇒ Σ1 → Σ2 → σ, thenΨ; Σ2 , Σ1 `AP e ⇒ Σ1 → Σ2 → σ.
The relationship between our system and standard Hindley Milner type system (HM) canbe established through the desugaring of let expressions. Namely, if e is typeable in HM, thenthe desugared expression e′ is typeable in our system, with a more general typing result.
Lemma 3.4 (AP Conservative over HM). If Ψ `HM e : σ, and desugaring let expression in e
gives back e′ , then for some σ ′ , we have Ψ `AP e′ ⇒ σ ′ , and σ ′ <: σ.

3.2.3 Subtyping
We present our subtyping rules at the bottom of Figure 3.2. Interestingly, our subtyping has
two different forms.
Subtyping. The first subtyping judgment `AP σ1 <: σ2 follows OL, but in HM-style; thatis, without tracking type variables. Rule ap-s-forallR states σ1 is subtype of ∀a. σ2 only
if σ1 is a subtype of σ2 , with the assumption a is a fresh variable. Rule ap-s-forallL says∀a. σ1 is a subtype of σ2 if we can instantiate it with some τ and show the result is a subtypeof σ2 .
Application Subtyping. The typing rule ap-app-var uses the second subtyping judgment Σ `AP σ1 <: σ2 . To motivate this new kind of judgment, consider the expression id 1
for example, whose derivation is stuck at rule ap-app-var (here we assume id : ∀a. a → a ∈Ψ):id : ∀a. a → a ∈ Ψ ???
Ψ `AP 1 ⇒ Int ∅ = fv (Int) − fv (Ψ)Ψ; Int `AP id ⇒ ?

Ψ `AP id 1 ⇒ ?

ap-app-var
ap-app-app

Here we know that id : ∀a. a → a and also, from the application context, that id is applied
to an argument of type Int. Thus we need a mechanism for solving the instantiation a = Int
and returning a supertype Int → Int as the type of id. This is precisely what the application
subtyping achieves: resolving instantiation constraints according to the application context.
Notice that unlike existing works (Peyton Jones et al. [2007] or DK), application subtyping
provides a way to solve instantiation more locally, since it does not mutually depend on typing.

45

3 Higher-Rank Polymorphism with the Application Mode
Back to the rules in Figure 3.2, one way to understand the judgment Σ `AP σ1 <: σ2 froma computational point-of-view is that the type σ2 is a computed output, rather than an input.
In other words σ2 is determined from Σ and σ1 . This is unlike the judgment `AP σ1 <: σ2 ,where both σ1 and σ2 would be computationally interpreted as inputs. Therefore it is notpossible to view `AP σ1 <: σ2 as a special case of Σ `AP σ1 <: σ2 where Σ is empty.
There are three rules dealing with application contexts. Rule ap-as-empty is for case when
the application context is empty. Because it is empty, we have no constraints on the type, so wereturn it back unchanged. Note that this is where HM-style systems (also Peyton Jones et al.
[2007]) would normally use an instantiation rule (e.g. rule hm-inst in HM) to remove toplevel quantifiers. Our system does not need the instantiation rule, because in applications,type information flows from arguments to the function, instead of function to arguments.
In the latter case, the instantiation rule is needed because a function type is wanted insteadof a polymorphic type. In our approach, instantiation of type variables is avoided unlessnecessary.
The two remaining rules apply when the application context is non-empty, for polymorphic and function types respectively. Note that we only need to deal with these two cases
because Int or type variables a cannot have a non-empty application context. In rule ap-asforall, we instantiate the polymorphic type with some τ , and continue. This instantiation
is forced by the application context. In rule ap-as-arrow, one function of type σ1 → σ2
is now being applied to an argument of type σ3 . So we check `AP σ3 <: σ1 . Then we continue with σ2 and the rest application context, and return σ3 → σ4 as the result type of thefunction.
Metatheory. Application subtyping is novel in our system, and it enjoys some interesting
properties. For example, As with typing, the application context decides the form of thesupertype.
Lemma 3.5 (Σ Coincides with Subtyping Results). If Σ `AP σ1 <: σ2 , then for some σ3 ,σ2 = Σ → σ3 .
Therefore we can always use the judgment Σ `AP σ1 <: Σ → σ2 , instead of Σ `AP σ1 <:σ2 . Application subtyping is also reflexive and transitive. Interestingly, in those lemmas, if weremove all applications contexts, they are exactly the reflexivity and transitivity of traditional
subtyping.
Lemma 3.6 (Reflexivity of Application Subtyping). Σ `AP Σ → σ <: Σ → σ.
Lemma 3.7 (Transitivity of Application Subtyping). If Σ1 `AP σ1 <: Σ1 → σ2 , and Σ2 `APσ2 <: Σ2 → σ3 , then Σ2 , Σ1 `AP σ1 <: Σ1 → Σ2 → σ3 .

46

3.3 Type-directed Translation
Finally, we can convert between subtyping and application subtyping. We can remove theapplication context and still get a subtyping relation:
Lemma 3.8 (Σ `AP <: to `AP <:). If Σ `AP σ1 <: σ2 , then `AP σ1 <: σ2 .
Transferring from subtyping to application subtyping will result in a more general type.
Lemma 3.9 (`AP <: to Σ `AP <:). If `AP σ1 <: Σ → σ2 , then for some σ3 , we have Σ `AP
σ1 <: Σ → σ3 , and `AP σ3 <: σ2 .
This lemma may not seem intuitive at first glance. Consider a concrete example. Consider
the derivation for `AP Int → ∀a. a <: Int → Int:

`AP Int <: Int

ap-s-int`AP Int <: Int

ap-s-int`AP ∀a. a <: Int

ap-s-forallL
ap-s-arrow`AP Int → ∀a. a <: Int → Int
and for Int `AP Int → ∀a. a <: Int → ∀a. a:`AP Int <: Int

ap-s-int`AP ∀a. a <: ∀a. a

Int `AP Int → ∀a. a <: Int → ∀a. a

ap-as-empty
ap-as-arrow

The former one, holds because we have `AP ∀a. a <: Int in the return type. But in the latter
one, after Int is consumed from application context, we eventually reach rule ap-as-empty,
which always returns the original type back.

3.3 Type-directed Translation
This section discusses the type-directed translation of System AP into a variant of System Fthat is also used in Peyton Jones et al. [2007]. The translation is shown to be coherent andtype safe. The later result implies the type-safety of the source language. To prove coherency,
we need to decide when two translated terms are the same using η-id equality, and show thatthe translation is unique up to this definition.

3.3.1 Target Language
The syntax and typing rules of our target language are given in Figure 3.3.

47

3 Higher-Rank Polymorphism with the Application Mode

Expressions
Types
Contexts

s, f
σ
Ψ

::=
::=
::=

x | n | λx : σ. s | Λa. s | s1 s2 | s σ
Int | a | σ1 → σ2 | ∀a. σ• | Ψ, x : σΨ `F s : σ(Typing)

f-var

f-lamann(x : σ) ∈ Ψ

f-intΨ `F x : σΨ `F n : Intf-app

Ψ `F s 1 : σ 1 → σ 2

Ψ, x : σ1 `F s : σ2Ψ `F s 2 : σ 1Ψ `F λx : σ1 . s : σ1 → σ2
f-tabsΨ `F s : σΨ `F s 1 s 2 : σ 2a ∈/ fv (Ψ)Ψ `F Λa. s : ∀a. σ

f-tappΨ `F s : ∀a. σ1Ψ `F s σ2 : σ1 [a 7→ σ2 ]Figure 3.3: Syntax and typing rules of System F.

Expressions include variables x, integers n, annotated abstractions λx : σ. s, type-level
abstractions Λa. s, and s1 s2 for term application, and s σ for type application. The types
and the typing contexts are the same as our system, where typing contexts do not track typevariables. In translation, we use f to refer to the coercion function produced by the subtyping
translation, and s to refer to the translated term in System F.
Most typing rules are straightforward. Rule f-tabs types a type abstraction with explicit
generalization, while rule f-tapp types a type application with explicit instantiation.

3.3.2 Subtyping Coercions
The type-directed translation of subtyping is shown in Figure 3.4. The translation follows thesubtyping relations from Figure 3.2, but adds a new component. The judgment `AP σ1 <:σ2 ⇝ f is read as: if `AP σ1 <: σ2 holds, it can be translated to a coercion function f in
System F. The coercion function produced by subtyping is used to transform values from one
type to another, so we should have • `F f : σ1 → σ2 .
Rule ap-s-int and rule ap-s-tvar produce identity functions, since the source type andtarget type are the same. In rule ap-s-arrow, the coercion function f1 of type σ3 → σ1 isapplied to y to get a value of type σ1 . Then the resulting value becomes an argument to x,
and a value of type σ2 is obtained. Finally we apply f2 to the value of type σ2 , so that a value48

3.3 Type-directed Translation`AP σ1 <: σ2 ⇝ f(Subtyping Translation)

ap-s-tvar

ap-s-int`AP a <: a ⇝ λx : a. x

`AP Int <: Int ⇝ λx : Int. x

ap-s-arrow`AP σ3 <: σ1 ⇝ f1

`AP σ2 <: σ4 ⇝ f2

`AP σ1 → σ2 <: σ3 → σ4 ⇝ λx : σ1 → σ2 . λy : σ3 . f2 (x (f1 y))
ap-s-forallL

ap-s-forallR`AP σ1 [a 7→ τ ] <: σ2 ⇝ f

a ∈/ fv (σ1 )`AP ∀a. σ1 <: σ2 ⇝ λx : ∀a. σ1 . f (x τ )`AP σ1 <: σ2 ⇝ f`AP σ1 <: ∀a. σ2 ⇝ λx : σ1 . Λa. f xΣ `AP σ1 <: σ2 ⇝ f(Application Subtyping)
ap-as-forall

Σ, σ3 `AP σ1 [a 7→ τ ] <: σ2 ⇝ f

ap-as-empty• `AP σ <: σ ⇝ λx : σ. x
ap-as-arrowΣ, σ3 `AP ∀a. σ1 <: σ2 ⇝ λx : ∀a. σ1 . f (x τ )`AP σ3 <: σ1 ⇝ f1

Σ `AP σ2 <: σ4 ⇝ f2

Σ, σ3 `AP σ1 → σ2 <: σ3 → σ4 ⇝ λx : σ1 → σ2 . λy : σ3 . f2 (x (f1 y))

Figure 3.4: Subtyping translation rules of System AP.

49

3 Higher-Rank Polymorphism with the Application ModeΨ `AP e ⇒ σ ⇝ s(Typing Inference)
ap-inf-lam

ap-inf-int

Ψ, x : τ `AP e ⇒ σ ⇝ sΨ `AP n ⇒ Int ⇝ nΨ `AP λx. e ⇒ τ → σ ⇝ λx : τ. s

ap-inf-lamann

Ψ, x : σ1 `AP e ⇒ σ2 ⇝ sΨ `AP λx : σ1 . e ⇒ σ1 → σ2 ⇝ λx : σ1 . s
Ψ; Σ `AP e ⇒ σ ⇝ s
ap-app-var(x : σ1 ) ∈ Ψ(Typing Application Mode)Σ `AP σ1 <: σ2 ⇝ f

Ψ; Σ `AP x ⇒ σ2 ⇝ f x
ap-app-lamann`AP σ2 <: σ1 ⇝ f

ap-app-lam

Ψ, x : σ1 ; Σ `AP e ⇒ σ2 ⇝ sΨ; Σ, σ1 `AP λx. e ⇒ σ1 → σ2 ⇝ λx : σ1 . s
Ψ, x : σ1 `AP e ⇒ σ3 ⇝ s

Ψ; Σ, σ2 `AP λx : σ1 . e ⇒ σ2 → σ3 ⇝ λy : σ2 . (λx : σ1 . s) (f y)ap-app-app

Ψ `AP e2 ⇒ σ1 ⇝ s2
ai i = fv (σ1 ) − fv (Ψ)σ2 = ∀ai i . σ1APΨ; Σ, σ2 ` e1 ⇒ σ2 → σ3 ⇝ s1
Ψ; Σ `AP e1 e2 ⇒ σ3 ⇝ s1 (Λai i . s2 )Figure 3.5: Typing translation rules of System AP.

50

3.3 Type-directed Translationof type σ4 is computed. In rule a-ps-ForallL, the input argument is a polymorphic type, so
we feed the type τ to it and apply the coercion function f from the precondition. Rule ap-sforallR uses the coercion f and, in order to produce a polymorphic type, we add one typeabstraction to turn it into a coercion of type σ1 → ∀a. σ2 .
The second part of the subtyping translation deals with coercions generated by application
subtyping. The judgment Σ `AP σ <: σ2 ⇝ f is read as: if Σ `AP σ <: σ2 holds, it canbe translated to a coercion function f of type σ → σ2 in System F. If we compare two parts,
we can see application contexts play no role in the generation of the coercion. Notice thesimilarity between rule ap-s-tvar and rule ap-as-empty, between rule ap-s-forallR andrule ap-as-forall, and between rule ap-s-arrow and rule ap-as-arrow. We therefore
omit more explanations.

3.3.3 Type-Directed Translation of Typing
The type directed translation of typing is shown in the Figure 3.5, which extends the rules in
Figure 3.1. The judgment Ψ; Σ `AP e ⇒ σ ⇝ s is read as: if Ψ; Σ `AP e ⇒ σ holds, theexpression can be translated to term s in System F. The judgment Ψ `AP e ⇒ σ ⇝ s is the
special case when the application context is empty.
Most translation rules are straightforward. In rule ap-app-var, x is translated to f x,
where f is the coercion function generated from subtyping. Rule ap-app-lamann applies
the coercion function f to y, then feeds y to the function generated from the abstraction.
When generalizing over a type, rule ap-app-app generate type-level abstractions.

3.3.4 Type Safety
We prove that our system is type safe by proving that the translation produces well-typed
terms in System F [Girard 1986], and it is known that well-typed terms in System F is typesafe. In particular, type safety ensures that a well-typed program cannot go wrong [Wrightand Felleisen 1994].
Lemma 3.10 (Soundness of Typing Translation). If Ψ; Σ `AP e ⇒ σ ⇝ s , then Ψ `F s : σ.
The lemma relies on the translation of subtyping to produce type-correct coercions.
Lemma 3.11 (Soundness of Subtyping Translation).
1. If `AP σ <: σ2 ⇝ f , then • `F f : σ → σ2 .
2. If Σ `AP σ <: σ2 ⇝ f , then • `F f : σ → σ2 .

51

3 Higher-Rank Polymorphism with the Application Mode|x||n||λx : σ. s|

=
=
=

|x|
|n|
λx. |s||Λa. s||s1 s2 |
|s σ|

=
=
=

|s|
|s1 | |s2 ||s|(Eta-id Equality)

f1 =ηid f2
eta-reduce

eta-appx ∈/ fv (f )eta-idλx. f x =ηid f(λx. x) f =ηid f

eta-lameta-refl

f =ηid f ′λx. f =ηid λx. f′

f =ηid f

f2 =ηid f2′

f1 =ηid f1′

eta-symm

f =ηid f ′′f =ηid f

f1 f2 =ηid f1′ f2′

eta-trans

f1 =ηid f2

f2 =ηid f3

f1 =ηid f3

Figure 3.6: Type erasure and eta-id equality of System F.

3.3.5 Coherence
One problem with the translation is that there are multiple targets corresponding to one expression. This is because in original system there are multiple choices when instantiating a
polymorphic type, or guessing the annotation for unannotated lambda abstraction (rule apinf-lam). For each choice, the corresponding target will be different. For example, expression λx. x can be type checked with Int → Int, or a → a, and the corresponding targets are
λx : Int. x, and λx : a. x.
Therefore, in order to prove the translation is coherent, we turn to prove that all the translations have the same operational semantics. There are two steps towards the goal: typeerasure, and considering η expansion and identity functions.

Type Erasure. Since type information is useless after type-checking, we erase the type information of the targets for comparison. The erasure process is given at the top of Figure 3.6.
The erasure process is standard, where we erase the type annotation in abstractions, andremove type abstractions and type applications. The calculus after erasure is the untyped
lambda calculus.

Eta-id Equality. However, even if we have type erasure, multiple targets for one expression can still be syntactically different. The problem is that we can insert more coercion
functions in one translation than another, since an expression can have a more polymorphic

52

3.4 Type Inference Algorithm
type in one derivation than another one. So we need a more refined definition of equality
instead of syntactic equality.
We use a similar definition of eta-id equality as in Chen [2003], given in Figure 3.6. In =ηid
equality, two expressions are regarded as equivalent if they can turn into the same expression
through η-reduction or removal of redundant identity functions. The =ηid relation is reflexive, symmetrical, and transitive. As a small example, we can show that λx. (λy. y) f x =ηid f .

eta-refl

f =ηid f(λy. y) f =ηid f

eta-idλx. (λy. y) f x =ηid f

eta-reduce

Now we first prove that the erasure of the translation result of subtyping is always =ηid toan identity function.
Lemma 3.12 (Subtyping Coercions eta-id equal to id).
• if `AP σ1 <: σ2 ⇝ f , then |f | =ηid λx. x.
• if Σ `AP σ1 <: σ2 ⇝ f , then |f | =ηid λx. x.
We then prove that our translation actually generates a unique target:
Lemma 3.13 (Coherence). If Ψ1 ; Σ1 `AP e ⇒ σ ⇝ s1 , and Ψ2 ; Σ2 `AP e ⇒ σ2 ⇝ s2 ,then |s1 | =ηid |s2 |.

3.4 Type Inference Algorithm
Even though our specification is syntax-directed, it does not directly lead to an algorithm,
because there are still many guesses in the system, such as in rule ap-inf-lam. This subsection presents a brief introduction of the algorithm, which closely follows the approach by
Peyton Jones et al. [2007]. The full rules of the algorithm can be found in Appendix A.
Instead of guessing, the algorithm creates meta type variables αb, βb which are waiting to besolved. The judgment for the algorithmic type system is(S1 , N1 ); Ψ `AP e ⇒ σ ,→ (S2 , N2 )Here we use N as name supply, from which we can always extract new names. Also, every
time a meta type variable is solved, we need to record its solution. We use S as a notation for53

3 Higher-Rank Polymorphism with the Application Mode
the substitution that maps meta type variables to their solutions. For example, rule ap-inflam becomes

ap-a-inf-lam(S0 , N0 ); Ψ, x : βb `AP e ⇒ σ ,→ (S1 , N1 )b Ψ `AP λx. e ⇒ βb → σ ,→ (S1 , N1 )(S0 , N0 β);Comparing it to rule ap-inf-lam, τ is replaced by a new meta type variable βb from nameb But despite of the name supply and substitution, the rule retains the structure
supply N0 β.
of rule ap-inf-lam.
Having the name supply and substitutions, the algorithmic system is a direct extension of
the specification in Figure 3.2, with a process to do unifications that solve meta type variables.
Such unification process is quite standard and similar to the one used in the Hindley-Milner
system. We proved our algorithm is sound and complete with respect to the specification.
Theorem 3.14 (Soundness). If ([], N0 ); Ψ `AP e ⇒ σ ,→ (S1 , N1 ), then for any substitution
V with dom(V ) = fv (S1 Ψ, S1 σ), we have V S1 Ψ `AP e ⇒ V S1 σ.
Theorem 3.15 (Completeness). If Ψ `AP e ⇒ σ, then for a fresh N0 , we have ([], N0 ); Ψ `APie ⇒ σ2 ,→ (S1 , N1 ), and for some S2 , if ai i = fv (Ψ) − fv(S2 S1 σ2 ), and bi = fv (Ψ) −i

fv (σ), we have `AP ∀ai i .S2 S1 σ2 <: ∀bi .σ.

3.5 Discussion
3.5.1 Combining Application and Checking Modes
Although the application mode provides us with alternative design choices in a bidirectional
type system, a checking mode can still be easily added. One motivation for the checking
mode would be annotated expressions e : σ, where the type of the expression is known andis therefore used to check the expression, as in DK.
Consider adding e : σ for introducing the checking mode for the language. Notice that,since the checking mode is stronger than the application mode, when entering the checking
mode the application context is no longer useful. Instead we use application subtyping tosatisfy the application context requirements. A possible typing rule for annotated expressionsis:ap-app-annoΨ `AP e ⇐ σ1Σ `AP σ1 <: σ2Ψ; Σ `AP e : σ1 ⇒ σ2Here, e is checked using its annotation σ1 , and then we instantiate σ1 to σ2 using application
subtyping with the application context Σ.

54

3.5 Discussion
Now we can have a rule set of the checking mode for all expressions, much like thosechecking rules in DK. For example, one useful rule for abstractions in the checking mode
could be rule ap-chk-lam, where the parameter type σ1 serves as the type of x, and typing
checks the body with σ2 .

ap-chk-lam

Ψ, x : σ1 `AP e ⇐ σ2Ψ `AP λx. e ⇐ σ1 → σ2
Moreover, combined with the information flow, the checking rule for application checks
the function with the full type.
ap-chk-app

Ψ `AP e2 ⇒ σ1Ψ `AP e1 ⇐ σ1 → σ2Ψ `AP e1 e2 ⇐ σ2
Note that adding annotated expressions might bring convenience for programmers, sinceannotations can be more freely placed in a program. For example, (λx. x 1) : (Int → Int) →Int becomes valid. However this does not add any expressive power, since annotated expressions that are typeable would remain typeable after moving the annotations to bindings. For
example the previous program is equivalent to (λx : Int → Int. x 1).
This discussion is a sketch. We have not defined the corresponding declarative system
nor algorithm. However we believe that the addition of the checking mode will not bringsurprises to the meta-theory.

3.5.2 Additional Constructs
In this section, we show that the application mode is compatible with other constructs, bydiscussing how to add support for pairs in the language. A similar methodology would apply
to other constructs like sum types, data types, if-then-else expressions and so on.
The introduction rule for pairs must be in the inference mode with an empty application
context. Also, the subtyping rule for pairs is as expected.
ap-s-pair

ap-inf-pairΨ `AP e1 ⇒ σ1Ψ `AP e2 ⇒ σ2Ψ `AP (e1 , e2 ) ⇒ (σ1 , σ2 )`AP σ1 <: σ3`AP σ2 <: σ4`AP (σ1 , σ2 ) <: (σ3 , σ4 )The application mode can apply to the elimination constructs of pairs. If one componentof the pair is a function, for example, fst (λx. x, 1) 2, then it is possible to have a judgment55

3 Higher-Rank Polymorphism with the Application Modewith a non-empty application context. Therefore, we can use the application subtyping toaccount for the application contexts:
ap-app-fst

Ψ `AP e ⇒ (σ1 , σ2 )Σ `AP σ1 <: σ3Ψ; Σ `AP fst e ⇒ σ3
ap-app-snd

Ψ `AP e ⇒ (σ1 , σ2 )Σ `AP σ2 <: σ3Ψ; Σ `AP snd e ⇒ σ3
However, in polymorphic type systems, we need to take the subsumption rule into consideration. For example, in the expression (λx : ∀a. (a, b). fst x), fst is applied to a polymorphic
type. Interestingly, instead of a non-deterministic subsumption rule, having polymorphic
types actually leads to a simpler solution. According to the philosophy of the applicationmode, the types of the arguments always flow into the functions. Therefore, instead of regarding fst e as an expression form, where e is itself an argument, we could regard fst as afunction on its own, whose type is ∀a. ∀b. (a, b) → a. Then as in the variable case, we use
the subtyping rule to deal with application contexts. Thus the typing rules for fst and snd
can be modeled as:
ap-app-fst-var

ap-app-snd-varΣ`Σ `AP ∀a. ∀b. (a, b) → b <: σAP∀a. ∀b. (a, b) → a <: σΨ; Σ `AP fst ⇒ σΨ; Σ `AP snd ⇒ σNote that another way to model those two rules would be to simply have an initial typing
environment Ψinit ≡ fst : ∀a. ∀b. (a, b) → a, snd : ∀a. ∀b. (a, b) → b. In this case theelimination of pairs be dealt directly by the rule for variables.
An extended version of the calculus extended with rules for pairs (rule ap-inf-pair, rule aps-pair, rule ap-app-fst-var and rule ap-app-snd-var), has been formally studied. All thetheorems presented before hold with the extension of pairs.

3.5.3 More Expressive Type Applications
The design choice of propagating arguments to functions was subject to consideration in
the original work on local type inference [Pierce and Turner 2000], but was rejected due topossible non-determinism introduced by explicit type applications:“It is possible, of course, to come up with examples where it would be beneficial tosynthesize the argument types first and then use the resulting information to avoid56

3.5 Discussion
type annotations in the function part of an application expression....Unfortunately
this refinement does not help infer the type of polymorphic functions. For example,
we cannot uniquely determine the type of x in the expression (fun[X](x) e) [Int] 3.”
As a response to this challenge, we also present an application of the application mode to avariant of System F [Xie and Oliveira 2018]. The development of the calculus shows that theapplication mode can actually work well with calculi with explicit type applications. Here
we explain the key ideas of the design of the system, but refer to Xie and Oliveira [2018] formore details.
To explain the new design, consider the expression:(Λa.λx : a. x + 1) Int
which is not typeable in the traditional type system for System F. In System F the lambda
abstractions do not account for the context of possible function applications. Therefore whentype checking the inner body of the lambda abstraction, the expression x + 1 is ill-typed,
because all that is known is that x has the (abstract) type a.
If we are allowed to propagate type information from arguments to functions, then we canverify that a = Int and x + 1 is well-typed. The key insight in the new type system is to use
contexts to track type equalities induced by type applications. This enables us to type check
expressions such as the body of the lambda above (x + 1). The key rules for type abstractions
and type applications are:Ψ; Σ, [[Ψ]σ1 ] `AP e ⇒ σ2Ψ; Σ `AP e σ1 ⇒ σ2

ap-app-tapp

Ψ, a = σ1 ; Σ `AP e ⇒ σ2Ψ; Σ, [σ1 ] `AP Λa.e ⇒ σ2

ap-app-tlam

For type applications, rule ap-app-tapp stores the type argument σ1 into the application
context. Since Ψ tracks type equalities, we apply Ψ as a type substitution to σ1 (i.e., [Ψ]σ1 )Moreover, to distinguish between type arguments and types of term arguments, we put typearguments in brackets (i.e., [[Ψ]σ1 ]). For type abstractions (rule ap-app-tlam), if the application context is non-empty, we put a new type equality between the type variable a and thetype argument σ1 into the context.
Now, back to the problematic expression (fun[X](x) e) [Int] 3, the type of x can be inferred
as either X or Int since they are actually equivalent.
Sugar for Type Synonyms. In the same way that we can regard let expressions as syntactic
sugar, in the new type system we further gain built-in type synonyms. A type synonym is a57

3 Higher-Rank Polymorphism with the Application Mode
new name for an existing type. Type synonyms are common in languages such as Haskell.
In our calculus a simple form of type synonyms can be desugared as follows:
type a = σ in e ⇝ (Λa.e)σOne practical benefit of such syntactic sugar is that it enables a direct encoding of a System Flike language with declarations (including type-synonyms). Although declarations are often
viewed as a routine extension to a calculus, and are not formally studied, they are highly
relevant in practice. Therefore, a more realistic formalization of a programming language
should directly account for declarations. By providing a way to encode declarations, ournew calculus enables a simple way to formalize declarations.
Type Abstraction. The type equalities introduced by type applications may seem like
we are breaking System F type abstraction. However, we argue that type abstraction is still
supported by our System F variant. For example:
let inc = Λa.λx : a. x + 1 in inc Int 1(after desugaring) does not type-check, as in a System-F like language. In our type system
lambda abstractions that are immediatelly applied to an argument, and unapplied lambda
abstractions behave differently. Unapplied lambda abstractions are just like System F abstractions and retain type abstraction. The example above illustrates this. In contrast the
typeable example (Λa.λx : a. x + 1) Int, which uses a lambda abstraction directly applied toan argument, can be regarded as the desugared expression for type a = Int in λx : a. x + 1.

58

Part III
Higher-Rank Polymorphism and Gradual
Typing59

4 Gradually Typed Higher-Rank
Polymorphism

Gradual typing [Siek and Taha 2006] is an increasingly popular topic in both programming language practice and theory. On the practical side there is a growing number of programming languages adopting gradual typing. Those languages include Clojure [BonnaireSergeant et al. 2016], Python [Lehtosalo et al. 2006; Vitousek et al. 2014], TypeScript [Bierman et al. 2014], Hack [Verlaguet 2013], and the addition of Dynamic to C# [Bierman et al.
2010], to name a few. On the theoretical side, recent years have seen a large body of research that defines the foundations of gradual typing [Cimini and Siek 2016, 2017; Garcia
et al. 2016], explores their use for both functional and object-oriented programming [Siekand Taha 2006, 2007], as well as its applications to many other areas [Bañados Schwerter
et al. 2014; Castagna and Lanvin 2017; Jafery and Dunfield 2017].
In this chapter, we present GPC, a gradually typed calculus for implicit higher-rank polymorphism. Integrating gradual typing with higher-rank polymorphism is challenging. In
particular, gradual typing calculi employ type consistency to validate type conversions. Polymorphic types à la System F also induce a subtyping relation that relates polymorphic types
to their instantiations. The original definition of consistent subtyping by Siek and Taha [2007]
serves as a guideline for designing gradual type systems with subtyping. However Siek andTaha’s definition is not adequate for polymorphic subtyping. Therefore, this section first proposes a generalization of consistent subtyping (Section 4.2) that is adequate for polymorphic
subtyping, and subsumes the original definition by Siek and Taha. The new definition of
consistent subtyping provides novel insights with respect to previous polymorphic gradualtype systems, which did not employ consistent subtyping.
We then develop GPC on top of our new notion of consistent subtyping. We develop
both declarative (Section 4.3) and bidirectional algorithmic versions (Section 4.4) for thetype system. The algorithmic version employs techniques developed by DK [Dunfield andKrishnaswami 2013] for higher-rank polymorphism to deal with instantiation.

61

4 Gradually Typed Higher-Rank Polymorphism
σ1 ∼ σ2(Type Consistency)
σ1 ∼ σ3

σ∼σ

σ∼?

?∼σ

σi ∼ σi′σ2 ∼ σ4iii[ li : σi ] ∼ [ li : σi′ ]σ1 → σ2 ∼ σ3 → σ4

(Subtyping)σ1 <: σ2
Int <: Int

Bool <: Boolσ3 <: σ1

σ2 <: σ4

σ1 → σ2 <: σ3 → σ4

Float <: Floatσi <: σi′i

Int <: Floatii[ li : σi ] <: [ li : σi′ ]? <: ?

Figure 4.1: Subtyping and type consistency in FOb?<:4.1 Introduction and Motivation
4.1.1 Background: Gradual Typing
As is well known, a gradually typed language supports both fully static and fully dynamic
checking of program properties, as well as the continuum between these two extremes. It
also offers programmers fine-grained control over the static-to-dynamic spectrum, i.e., aprogram can be evolved by introducing more or less precise types as needed [Garcia et al.
2016]. The untyped part of a program is dynamically checked, while the typed part is statically checked. Therefore, an expression like (λx : ?. x + 1) true is dynamically rejected, while
an expression like (λx : Int. x + 1) true is statically rejected. Here in the example we see theuse of the unknown type ?, which indicates missing of static type information and thus theuse of dynamic typing.
Gradual type calculi formalize the interaction between static types and dynamic type byfeaturing the unknown type ?, as well as a key concept, consistency, which weakens typeequality to allow for the presence of the unknown type ?. Essentially, if σ1 is consistent withσ2 (written σ1 ∼ σ2 ), then a function e1 expecting an argument of type σ1 can also accept
an argument e2 of type σ2 . Importantly, the unknown type ? is consistent with any type.
Therefore, the expression (λx : ?. x + 1) true type checks and only later gets rejected atruntime.
Siek and Taha [2007] developed a gradual type system for object-oriented languages thatthey call FOb?<: . The definition of type consistency, σ1 ∼ σ2 , is given at the top of Figure 4.1.

62

4.1 Introduction and Motivation
The intuition is that consistency relaxes the structure of a type system to tolerate unknown
positions in a gradual type. Consider some examples:
Int

∼

Int

Int

∼

?

?

∼

Int

Int → ?

∼

? → Int

FOb?<: also features subtyping. Siek and Taha defined the subtyping relation in a way thatstatic type safety is preserved. Their key insight is that the unknown type ? is neutral to subtyping, with only ? <: ?. The subtyping relation σ1 <: σ2 relations are defined in Figure 4.1.
With subtyping, if σ1 <: σ2 , then a function e1 expecting an argument of type σ1 can also
accept an argument e2 of type σ2 . For example, a function expecting Float can accept an
integer.
However, the orthogonality of consistency and subtyping does not lead to a deterministic
relation. Now the question is that if we know a function e1 expecting an argument of typeσ1 , and an argument e2 with type σ2 , how can we determine if e1 e2 is well-typed? To thisend, Siek and Taha defined consistent subtyping, such that e1 e2 is well-typed only if σ2 is aconsistent subtype of σ1 (written σ2 ≲ σ1 ). They defined algorithmic consistent subtyping
based on a restriction operator, written σ1 |σ2 that “masks off ” the parts of type σ1 that are
unknown in type σ2 . For example,
Int → Int|Bool→Bool=

Int → ?

Bool → ?|Int→Int=

Bool → ?

The definition of the restriction operator is given below:

σ|σ′ = case (σ, σ ′ ) of
| (_, ?) ⇒ ?
| (σ1 → σ2 , σ1′ → σ2′ ) ⇒ σ1 |σ1′ → σ2 |σ2′′
| ([l1 : σ1 , ... , ln : σn ], [l1 : σ1′ , ... , lm : σm]) if n ⩽ m ⇒ [l1 : σ1 |σ1′ , ..., ln : σn |σn′ ]′| ([l1 : σ1 , ... , ln : σn ], [l1 : σ1′ , ... , lm : σm]) if n > m ⇒ [l1 : σ1 |σ1′ , ..., lm : σm |σm′ , ..., ln : σn ]| (_, _) ⇒ σWith the restriction operator, consistent subtyping is simply defined as:
Definition 3 (Algorithmic Consistent Subtyping of Siek and Taha [2007]). σ1 ≲ σ2 ≡σ1 |σ2 <: σ2 |σ1 .

63

4 Gradually Typed Higher-Rank Polymorphism
A primary contribution of their work is to show that, as Siek and Taha [2007] put it, “gradual typing and subtyping are orthogonal and can be combined in a principled fashion”. To
compose subtyping and consistency, Siek and Taha showed a proposition that algorithmic
consistent subtyping is equivalent to two declarative definitions, which we refer to as the
strawman for declarative consistent subtyping because it servers as a good guideline on superimposing consistency and subtyping. Both definitions are non-deterministic because of
the intermediate type σ3 .
Definition 4 (Strawman for Declarative Consistent Subtyping).
1. σ1 ≲ σ2 if and only if σ1 ∼ σ3 and σ3 <: σ2 for some σ3 .
2. σ1 ≲ σ2 if and only if σ1 <: σ3 and σ3 ∼ σ2 for some σ3 .
In our later discussion, it will always be clear which definition we are referring to. For
example, we focus more on Definition 4 in Section 4.2.2, and more on Definition 3 in Section 4.2.5.

4.1.2 Motivation: Gradually Typed Higher-Rank Polymorphism
Haskell is a language renowned for its advanced type system, but it does not feature gradualtyping. Of particular interest to us is its support for implicit higher-rank polymorphism,
which is supported via explicit type annotations. In Haskell some programs that are safe at
run-time may be rejected due to the conservativity of the type system. For example, consider
again the example from Section 2.2:(\f. (f 1, f 'a')) (\x. x)This program is rejected by Haskell’s type checker because Haskell implements the HM
rule that a lambda-bound argument (such as f) can only have a monotype, i.e., the typechecker can only assign f the type Int → Int, or Char → Char, but not ∀a. a → a. Finding such manual polymorphic annotations can be non-trivial, especially when the program
scales up and the annotation is long and complicated.
Instead of rejecting the program outright, due to missing type annotations, gradual typing provides a simple alternative by giving f the unknown type ?. With this type the same
program type-checks and produces (1, 'a'). By running the program, programmers cangain more insight about its run-time behaviour (e.g., f behaves just like an identity function).
Then, with this insight, they can also give f a more precise type (∀a. a → a) a posteriori sothat the program continues to type-check via implicit polymorphism and also grants morestatic safety. In this paper, we envision such a language that combines the benefits of both
implicit higher-rank polymorphism and gradual typing.

64

4.1 Introduction and Motivation

4.1.3 Application: Efficient (Partly) Typed Encodings of ADTs
We illustrate two concrete applications of gradually typed higher-rank polymorphism related
to algebraic datatypes. The first application shows how gradual typing helps in defining Scottencodings of algebraic datatypes [Curry et al. 1958; Parigot 1992], which are impossible toencode in plain System F. The second application shows how gradual typing makes it easy tomodel and use heterogeneous containers.
Our calculus does not provide built-in support for algebraic datatypes (ADTs). Nevertheless, the calculus is expressive enough to support efficient function-based encodings of (optionally polymorphic) ADTs1 . This offers an immediate way to model algebraic datatypes
in our calculus without requiring extensions to our calculus or, more importantly, to its
target—the polymorphic blame calculus. While we believe that such extensions are possible,
they would likely require non-trivial extensions to the polymorphic blame calculus. Thus
the alternative of being able to model algebraic datatypes without extending the target calculus is appealing. The encoding also paves the way to provide built-in support for algebraic
datatypes in the source language, while elaborating them via the encoding into λB.

Church and Scott Encodings. It is well-known that polymorphic calculi such as System
F can encode datatypes via Church encodings. However these encodings have well-known
drawbacks [Koopman et al. 2014]. In particular, some operations are hard to define, andthey can have a time complexity that is greater than that of the corresponding functions
for built-in algebraic datatypes. A well-known example is the definition of the predecessor
function for Church numerals [Church 1941]. Its definition requires significant ingenuity(while it is trivial with built-in algebraic datatypes), and it has linear time complexity (versus
the constant time complexity for a definition using built-in algebraic datatypes).
An alternative to Church encodings are the so-called Scott encodings [Curry et al. 1958].
They address the two drawbacks of Church encodings: they allow simple definitions that
directly correspond to programs implemented with built-in algebraic datatypes, and thosedefinitions have the same time complexity to programs using algebraic datatypes.
Unfortunately, Scott encodings, or more precisely, their typed variant [Parigot 1992], cannot be expressed in System F: in the general case they require recursive types, which System
F does not support. However, with gradual typing, we can remove the need for recursive
types, thus enabling Scott encodings in our calculus.
1

In a type system with impure features, such as non-termination or exceptions, the encoded types can havevalid inhabitants with side-effects, which means we only get the lazy version of those datatypes.

65

4 Gradually Typed Higher-Rank Polymorphism
A Scott Encoding of Parametric Lists. Consider for instance the typed Scott encodingof parametric lists in a system with implicit polymorphism:
List a ≜ µL. ∀b. b → (a → L → b) → b
nil ≜ foldList a (λm. λc. m) : ∀a. List acons ≜ λx. λxs. foldList a (λm. λc. c x xs) : ∀a. a → List a → List aThis encoding requires both polymorphic and recursive types2 . Like System F, our calculus
only supports the former, but not the latter. Nevertheless, gradual types still allow us to use
the Scott encoding in a partially typed fashion. The trick is to omit the recursive type binderµL and replace the recursive occurrence of L by the unknown type ?:List? a ≜ ∀b. b → (a → ? → b) → b
As a consequence, we need to replace the term-level witnesses of the iso-recursion by explicit type annotations to respectively forget or recover the type structure of the recursive
occurrences:
foldList? a ≜ λx. x : (∀b. b → (a → List? a → b) → b) → List? aunfoldList? a ≜ λx. x : List? a → (∀b. b → (a → List? a → b) → b)
With the reinterpretation of fold and unfold as functions instead of built-in primitives, wehave exactly the same definitions of nil? and cons? .
Note that when we elaborate our calculus into the polymorphic blame calculus, the above
type annotations give rise to explicit casts. For instance, after elaboration foldList? a e resultsin the cast h(∀b. b → (a → List? a → b) → b) ,→ List? ais where s is the elaboration of e.
In order to perform recursive traversals on lists, e.g., to compute their length, we need afixpoint combinator like the Y combinator. Unfortunately, this combinator cannot be assigned a type in the simply typed lambda calculus or System F. Yet, we can still provide a
gradual type for it in our system.
fix ≜ λf. (λx : ?. f (x x)) (λx : ?. f (x x)) : ∀a. (a → a) → a
This allows us for instance to compute the length of a list.
length ≜ fix (λlen. λl. zero? (λxs. succ? (len xs)))2

Here we use iso-recursive types, but equi-recursive types can be used too.

66

4.1 Introduction and Motivation
Here zero? : Int? and succ? : Int? → Int? are the encodings of the constructors for natural
numbers Int? . In practice, for performance reasons, we could extend our language with aletrec construct in a standard way to support general recursion, instead of defining a fixpoint
combinator.
Observe that the gradual typing of lists still enforces that all elements in the list are of
the same type: a list of type List? a requires every element to have type a. For instance,a heterogeneous list like cons? zero? (cons? true? nil? ), is rejected because zero? : Int? andtrue? : Bool? have different types.

Heterogeneous Containers. Heterogeneous containers are datatypes that can store dataof different types, which is very useful in various scenarios. One typical application is thatan XML element is heterogeneously typed. Moreover, the result of a SQL query contains
heterogeneous rows.
In statically typed languages, there are several ways to obtain heterogeneous lists. For example, in Haskell, one option is to use dynamic types. Haskell’s library Data.Dynamic provides the special type Dynamic along with its injection toDyn and projection fromDyn. The
drawback is that the code is littered with toDyn and fromDyn, which obscures the programlogic. One can also use the HList library [Kiselyov et al. 2004], which provides strongly
typed data structures for heterogeneous collections. The library requires several Haskell extensions, such as multi-parameter classes [Peyton Jones et al. 1997] and functional dependencies [Jones 2000]. With fake dependent types [McBride 2002], heterogeneous vectors arealso possible with type-level constructors.
In our type system, with explicit type annotations that set the element types to the unknown type, we can disable the homogeneous typing discipline for the elements and get
gradually typed heterogeneous lists3 . Such gradually typed heterogeneous lists are akin to
Haskell’s approach with Dynamic types, but much more convenient to use since no injections and projections are needed, and the ? type is built-in and natural to use, with the cost
paid through gradual typing via explicit casts during runtime.
An example of such gradually typed heterogeneous collections is:l ≜ cons? (zero? : ?) (cons? (true? : ?) nil? )Here we annotate each element with type annotation ? and the type system is happy to typecheck that l : List? ?. Note that we are being meticulous about the syntax, but with proper
3This argument is based on the extended type system in Chapter 5.

67

4 Gradually Typed Higher-Rank Polymorphism
implementation of the source language, we could write more succinct programs akin to
Haskell’s syntax, such as [0, True].

4.2 Revisiting Consistent Subtyping
In this section we explore the design space of consistent subtyping. We start with the definitions of consistency and subtyping for polymorphic types, and compare with some relevant
work. We then discuss the design decisions involved in our new definition of consistent subtyping, and justify the new definition by demonstrating its equivalence with that of Siek andTaha [2007] and the AGT approach [Garcia et al. 2016] on simple types.
The syntax of types is given at the top of Figure 4.2. Types σ are either the integer type Int,
type variables a, function types σ1 → σ2 , universal quantification ∀a. σ, or the unknown
type ?. Note that monotypes τ contain all types other than the universal quantifier and theunknown type ?. We will discuss this restriction when we present the subtyping rules. Contexts Ψ are ordered lists of type variable declarations and term variables.

4.2.1 Consistency and Subtyping
We start by giving the definitions of consistency and subtyping for polymorphic types, andcomparing our definitions with the compatibility relation by Ahmed et al. [2009] and typeconsistency by Igarashi et al. [2017].
Consistency. The key observation here is that consistency, σ ∼ σ2 , is mostly a structural relation, except that the unknown type ? can be regarded as any type. In other words,
consistency is an equivalence relation lifted from static types to gradual types [Garcia et al.
2016]. Following this observation, we naturally extend the definition from Figure 4.1 withpolymorphic types, as shown in the middle of Figure 4.2. In particular a polymorphic type∀a. σ is consistent with another polymorphic type ∀a. σ2 if σ is consistent with σ2 .
Subtyping. We express the fact that one type is a polymorphic generalization of anotherby means of the subtyping judgment Ψ `G σ <: σ2 . Compared with the subtyping rulesof Odersky and Läufer [1996] in Figure 2.5, the only addition is the neutral subtyping of ?.
In rule gpc-s-forallL, the judgment Ψ `G σ checks whether all the type variables in σ arebound in the context Ψ. Notice that, in this rule, the universal quantifier is only allowed to
be instantiated with a monotype. According to the syntax in Figure 4.2, monotypes do notinclude the unknown type ?. This is because if we were to allow the unknown type to be used
for instantiation, we could have ∀a. a → a <: ? → ? by instantiating a with ?. Since ? → ? is68

4.2 Revisiting Consistent Subtyping

Types
Monotypes
Contexts

σ
τ
Ψ

::=
::=
::=

Int | a | σ1 → σ2 | ∀a. σ | ?
Int | a | τ1 → τ2
• | Ψ, x : σ | Ψ, aσ ∼ σ2(Type Consistency)
σ1 ∼ σ3

σ∼σ

σ∼?

?∼σ

σ2 ∼ σ4σ ∼ σ2

σ1 → σ2 ∼ σ3 → σ4∀a. σ ∼ ∀a. σ2Ψ `G σ <: σ2(Subtyping)a∈ΨΨ `G a <: a

gpc-s-tvarΨ `G Int <: IntΨ `G σ3 <: σ1Ψ `G σ2 <: σ4

gpc-s-arrowΨ `G σ1 → σ2 <: σ3 → σ4Ψ `G τ

Ψ `G σ[a 7→ τ ] <: σ2Ψ `G ∀a. σ <: σ2Ψ, a `G σ <: σ2

gpc-s-forallLΨ `G ? <: ?

Ψ `G σ <: ∀a. σ2

gpc-s-forallR

gpc-s-unknownΨ `G σ(Well-formedness of types)a∈ΨΨ `G Int

gpc-s-intΨ `G ?

Ψ `G aΨ `G σΨ `G σ 2Ψ `G σ → σ 2

Ψ, a `G σΨ `G ∀a. σ

Figure 4.2: Syntax of types, consistency, subtyping and well-formedness of types in declarative GPC.

69

4 Gradually Typed Higher-Rank Polymorphism
consistent with any functions σ1 → σ2 , for instance, Int → Bool, this means that we could
provide an expression of type ∀a. a → a to a function where the input type is supposed to be
Int → Bool. However, as we know, ∀a. a → a is definitely not compatible with Int → Bool.
Indeed, this does not hold in any polymorphic type systems without gradual typing. So thegradual type system should not accept it either. (This is the conservative extension property
that will be made precise in Section 4.3.3.)
Importantly there is a subtle distinction between a type variable and the unknown type,although they both represent a kind of “arbitrary” type. The unknown type stands for theabsence of type information: it could be any type at any instance. Therefore, the unknown
type is consistent with any type, and additional type-checks have to be performed at runtime.
On the other hand, a type variable indicates parametricity. In other words, a type variable canonly be instantiated to a single type. For example, in the type ∀a. a → a, the two occurrencesof a represent an arbitrary but single type (e.g., Int → Int, Bool → Bool), while ? → ? could
be an arbitrary function (e.g., Int → Bool) at runtime.

Comparison with Other Relations. In other polymorphic gradual calculi, consistency
and subtyping are often mixed up to some extent. In λB [Ahmed et al. 2009], the compatibility relation for polymorphic types is defined as follows:
σ1 ≺ σ2
σ1 ≺ ∀a. σ2Comp-AllR

σ1 [a 7→ ?] ≺ σ2
∀a. σ1 ≺ σ2Comp-AllL

Notice that, in rule Comp-AllL, the universal quantifier is always instantiated to ?. However, this way, λB allows ∀a. a → a ≺ Int → Bool, which as we discussed before might
not be what we expect. Indeed λB relies on sophisticated runtime checks to rule out such
instances of the compatibility relation a posteriori.
Igarashi et al. [2017] introduced the so-called quasi-polymorphic types for types that may
be used where a ∀-type is expected, which is important for their purpose of conservativity
over System F. Their type consistency relation, involving polymorphism, is defined as follows4 :σ ∼ σ2∀a. σ ∼ ∀a. σ24σ ∼ σ2σ2 6= ∀a. σ2′? ∈ Types(σ2 )∀a. σ ∼ σ2This is a simplified version. These two rules are presented in Section 3.1 in their paper as one of the key ideasof the design of type consistency, which are later amended with labels.

70

4.2 Revisiting Consistent Subtyping
Compared with our consistency definition in Figure 4.2, their first rule is the same as ours.
The second rule says that a non ∀-type can be consistent with a ∀-type only if it contains ?.
In this way, their type system is able to reject ∀a. a → a ∼ Int → Bool. However, in orderto keep conservativity, they also reject ∀a. a → a ∼ Int → Int, which is perfectly sensible
in their setting of explicit polymorphism. However with implicit polymorphism, we would
expect ∀a. a → a to be related with Int → Int, since a can be instantiated to Int.
Nonetheless, when it comes to interactions between dynamically typed and polymorphically typed terms, both relations allow ∀a. a → Int to be related with ? → Int for example(i.e., ∀a. a → Int ≺ ? → Int in λB and ∀a. a → Int ∼ ? → Int in Igarashi et al. [2017]),
which in our view, is a kind of (implicit) polymorphic subtyping combined with type consistency, and that should be derivable by the more primitive notions in the type system (insteadof inventing new relations). One of our design principles is that subtyping and consistency
are orthogonal, and can be naturally superimposed, echoing the opinion of Siek and Taha
[2007]. In this case, we can for example derive the relation between ∀a. a → Int and ? → Int
via an intermediate type Int → Int, and by the subtyping relation ∀a. a → Int <: Int → Int
and the consistency relation Int → Int ∼ ? → Int.

4.2.2 Towards Consistent Subtyping
With the definitions of consistency and subtyping, the question now is how to compose thetwo relations so that two types can be compared in a way that takes both relations into account.
Unfortunately, the strawman version of consistent subtyping (Definition 4) does not work
well with our definitions of consistency and subtyping for polymorphic types. Consider two
types: (∀a. a → Int) → Int, and (? → Int) → Int. The first type can only reach the second
type in one way (first by applying consistency, then subtyping), but not the other way, as
shown in Figure 4.3a. We use ???? to mean that we cannot find such a type. Similarly, there
are situations where the first type can only reach the second type by the other way (first
applying subtyping, and then consistency), as shown in Figure 4.3b.
What is worse, if those two examples are composed in a way that those types all appear
co-variantly, then the resulting types cannot reach each other in either way. For example,
Figure 4.3c shows two such types by putting a Bool type in the middle, and neither definitionof consistent subtyping in Definition 4 works.
Observations on Consistent Subtyping Based on Information Propagation. In
order to develop a correct definition of consistent subtyping for polymorphic types, we needto understand how consistent subtyping works. We first review two important properties of71

4 Gradually Typed Higher-Rank Polymorphism∼????

(? → Int) → Int<:

Int → Int<:(∀a. a → Int) → Int

∼
(a)

????

<:

(∀a. ? → Int) → Int∼∼

Int → ?
<:

∀a. a

∼

????

(b)

(((? → Int) → Int) → Bool) → (Int → ?)<:<:(((∀a. a → Int) → Int) → Bool) → (∀a. a)

∼

????

(c)
Figure 4.3: Examples that break the original definition of consistent subtyping.

subtyping: (1) subtyping induces the subsumption rule used in typing: if σ1 <: σ2 , thenan expression of type σ1 can be used where σ2 is expected; (2) subtyping is transitive: if
σ1 <: σ2 , and σ2 <: σ3 , then σ1 <: σ3 . Though consistent subtyping takes the unknown
type into consideration, the subsumption rule should also apply: if σ1 ≲ σ2 , then an expression of type σ1 can also be used where σ2 is expected, given that there might be some
information lost by consistency. A crucial difference from subtyping is that consistent subtyping is not transitive because information can only be lost once; otherwise, any two types
are a consistent subtype of each other, e.g., we could derive Int ∼ Bool from Int ∼ ? and? ∼ Bool. Now consider a situation where we have both σ1 <: σ2 , and σ2 ≲ σ3 , this meansthat σ1 can be used where σ2 is expected, and σ2 can be used where σ3 is expected, withpossibly some loss of information. In other words, we should expect that σ1 can be used
where σ3 is expected, since there is at most one-time loss of information. We thus have thefollowing desirable property:
Observation 1. If σ <: σ2 , and σ2 ≲ σ3 , then σ ≲ σ3 .
This is reflected in Figure 4.4a. A symmetrical observation is given in Figure 4.4b:
Observation 2. If σ3 ≲ σ2 , and σ2 <: σ1 , then σ3 ≲ σ1 .
From the above observations, we see what the problem is with the original definition.
In Figure 4.4a, if σ2 can reach σ3 by σ ′ , then by subtyping transitivity, σ1 can reach σ3 by
σ ′ . However, if σ2 can only reach σ3 by σ ′′ , then σ cannot reach σ3 through the original
definition. A similar problem is shown in Figure 4.4b.
It turns out that these two problems can be fixed using the same strategy: instead of taking
one-step subtyping and one-step consistency, our definition of consistent subtyping allows

72

4.2 Revisiting Consistent Subtyping
σ′
<:
σ2

∼
≲
∼

σ1

σ3
≲

<:
σ ′′

≲

<:
σ′

≲

<:

<:
σ1

σ3
(a)

∼

∼

σ2
<:
σ ′′

(b)

Figure 4.4: Observations of consistent subtyping

types to take one subtyping step, one consistency step, and one more step of subtyping. Specifically, σ1 <: σ2 ∼ σ ′′ <: σ3 (in Figure 4.4a) and σ3 <: σ ′ ∼ σ2 <: σ1 (in Figure 4.4b) havethe same relation chain: subtyping, consistency, and subtyping.
Definition of Consistent subtyping. From the above discussion, we are ready to modify Definition 4, and adapt it to our notation:
Definition 5 (Consistent Subtyping). Ψ `G σ1 ≲ σ2 if and only if Ψ `G σ1 <: σ ′ , σ ′ ∼ σ ′′and Ψ `G σ ′′ <: σ2 for some σ ′ and σ ′′ .
With Definition 5, Figure 4.5 illustrates the correct relation chain for the broken example
shown in Figure 4.3c.
At first sight, Definition 5 seems worse than the original: we need to guess two types! It
turns out that Definition 5 is a generalization of Definition 4, and they are equivalent in thesystem of Siek and Taha [2007]. However, more generally, Definition 5 works well for withpolymorphic subtyping. Furthermore, as we shall see in Section 4.5.1, this definition also
works for top types (which are also problematic with the original definition).
Proposition 4.1 (Generalization of Declarative Consistent Subtyping).
• Definition 5 subsumes Definition 4.
In Definition 5, by choosing σ ′′ = σ2 , we have σ1 <: σ ′ and σ ′ ∼ σ2 ; by choosing
σ ′ = σ1 , we have σ1 ∼ σ ′′ , and σ ′′ <: σ2 .
• In the system of Siek and Taha, Definition 4 is equivalent to Definition 5 .
If σ1 <: σ ′ , σ ′ ∼ σ ′′ , and σ ′′ <: σ2 , by Definition 4, σ1 ∼ σ3 , σ3 <: σ ′′ for some σ3 .
By subtyping transitivity, σ3 <: σ2 . So σ1 ≲ σ2 by σ1 ∼ σ3 and σ3 <: σ2 .

73

4 Gradually Typed Higher-Rank Polymorphism

σ2

∼

<:

σ3
<:

σ1

≲

σ4

σ1 = (((∀a. a → Int) → Int) → Bool) → (∀a. a)
σ2 = (((∀a. a → Int) → Int) → Bool) → (Int → Int)σ3 = (((∀a. ? → Int) → Int) → Bool) → (Int → ?)σ4 = (((? → Int) → Int) → Bool) → (Int → ?)Figure 4.5: Example that is fixed by the new definition of consistent subtyping.

4.2.3 Abstracting Gradual Typing
Garcia et al. [2016] presented a new foundation for gradual typing that they call the Abstracting Gradual Typing (AGT) approach. In the AGT approach, gradual types are interpreted as
sets of static types, where static types refer to types containing no unknown types. In thisinterpretation, predicates and functions on static types can then be lifted to apply to gradualtypes. Central to their approach is the so-called concretization function. For simple types, a
concretization γ from gradual types to a set of static types is defined as follows:
Definition 6 (Concretization).
γ(Int)={Int}γ(σ1 → σ2 )={σ1′ → σ2′ | σ1′ ∈ γ(σ1 ), σ2′ ∈ γ(σ2 )}

γ(?)

=

{All static types}

Based on the concretization function, subtyping between static types can be lifted to gradual types, resulting in the consistent subtyping relation:
f: σ2 if and only if σ1′ <: σ2′ for some static
Definition 7 (Consistent Subtyping in AGT). σ1 <types σ1′ and σ2′ such that σ1′ ∈ γ(σ1 ) and σ2′ ∈ γ(σ2 ).

Later they proved that this definition of consistent subtyping coincides with that of Definition 4. By Proposition 4.1, we can directly conclude that our definition coincides withAGT:
f: σ2 .
Corollary 4.2 (Equivalence to AGT on Simple Types). σ1 ≲ σ2 if and only if σ1 <However, AGT does not show how to deal with polymorphism (e.g. the interpretation oftype variables) yet. Still, as noted by Garcia et al. [2016], this is a promising line of future
work for AGT, and the question remains whether our definition would coincide with it.

74

4.2 Revisiting Consistent Subtyping
Another note related to AGT is that the definition is later adopted by Castagna and Lanvin[2017] in a gradual type system with union and intersection types, where the static types
σ1′ , σ2′ in Definition 7 can be algorithmically computed by also accounting for top and bottom
types.

4.2.4 Directed Consistency
Directed consistency [Jafery and Dunfield 2017] is defined in terms of precision and subtyping:σ1′ v σ1

σ1 <: σ2σ2′ v σ2σ1′ ≲ σ2′
The judgment σ1 v σ2 is read “σ1 is less precise than σ2 ”.5 In their setting, precision is
first defined for type constructors and then lifted to gradual types, and subtyping is definedfor gradual types. If we interpret this definition from the AGT point of view, finding a more
precise static type has the same effect as concretization. Namely, σ1′ v σ1 implies σ1 ∈γ(σ1′ ) and σ2′ v σ2 implies σ2 ∈ γ(σ2′ ) if σ1 and σ2 are static types. Therefore we consider
this definition as AGT-style. From this perspective, this definition naturally coincides with
Definition 7, and by Corollary 4.2, it coincides with Definition 5.
The value of their definition is that consistent subtyping is derived compositionally from
gradual subtyping and precision. Arguably, gradual types play a role in both definitions,which is different from Definition 5 where subtyping is neutral to unknown types. Still,
the definition is interesting as it takes precision into consideration, rather than consistency.
Then a question arises as to how are consistency and precision related.
Consistency and Precision. Precision is a partial order (anti-symmetric and transitive),
while consistency is symmetric but not transitive. Recall that consistency is in fact an equivalence relation lifted from static types to gradual types [Garcia et al. 2016], which embodies the key role of gradual types in typing. Therefore defining consistency independently isstraightforward, and it is theoretically viable to validate the definition of consistency directly.
On the other hand, precision is usually connected with the gradual criteria [Siek et al. 2015],
and finding a correct partial order that adheres to the criteria is not always an easy task. For
example, Igarashi et al. [2017] argued that term precision for gradual System F is actuallynontrivial, leaving the gradual guarantee of the semantics as a conjecture. Thus precision
can be difficult to extend to more sophisticated type systems, e.g. dependent types.
5

Jafery and Dunfield actually read σ1 ⊑ σ2 as “σ1 is more precise than σ2 ”. We, however, use the “less precise”
notation (which is also adopted by Cimini and Siek [2016] ) throughout this work. The full rules can befound in Figure 4.8.

75

4 Gradually Typed Higher-Rank Polymorphism
Nonetheless, in our system, precision and consistency can be related by the following
lemma:
Lemma 4.3 (Consistency and Precision).
• If σ1 ∼ σ2 , then there exists (static) σ3 , such that σ1 v σ3 , and σ2 v σ3 .
• If for some (static) σ3 , we have σ1 v σ3 , and σ2 v σ3 , then we have σ1 ∼ σ2 .

4.2.5 Consistent Subtyping Without Existentials
Definition 5 serves as a fine specification of how consistent subtyping should behave in general. But it is inherently non-deterministic because of the two intermediate types σ ′ and σ ′′ .
As Definition 3, we need a combined relation to directly compare two types. A natural attempt is to try to extend the restriction operator for polymorphic types. Unfortunately, as
we show below, this does not work. However it is possible to devise an equivalent inductive
definition instead.
Attempt to Extend the Restriction Operator. Suppose that we try to extend Definition 3 to account for polymorphic types. The original restriction operator is structural,meaning that it works for types of similar structures. But for polymorphic types, two input types could have different structures due to universal quantifiers, e.g, ∀a. a → Int and(Int → ?) → Int. If we try to mask the first type using the second, it seems hard to maintainthe information that a should be instantiated to a function while ensuring that the return
type is masked. There seems to be no satisfactory way to extend the restriction operator inorder to support this kind of non-structural masking.
Interpretation of the Restriction Operator and Consistent Subtyping. If the restriction operator cannot be extended naturally, it is useful to take a step back and revisit what
the restriction operator actually does. For consistent subtyping, two input types could haveunknown types in different positions, but we only care about the known parts. What the
restriction operator does is (1) erase the type information in one type if the corresponding
position in the other type is the unknown type; and (2) compare the resulting types using
the normal subtyping relation. The example below shows the masking-off procedure for thetypes Int → ? → Bool and Int → Int → ?. Since the known parts have the relation that
Int → ? → ? <: Int → ? → ?, we conclude that Int → ? → Bool ≲ Int → Int → ?.
Int →

?

→ Bool | Int → Int → ?

Int → Int →76?

| Int → ? → Bool

= Int → ? → ?
= Int → ? → ?

<:4.3 Gradually Typed Implicit PolymorphismΨ `G σ 1 ≲ σ 2(Consistent Subtyping)

gpc-cs-tvara∈Ψ

gpc-cs-int

Ψ `G a ≲ aΨ `G Int ≲ Int

gpc-cs-forallLΨ `G τ
Ψ `G σ1 [a 7→ τ ] ≲ σ2Ψ ` ∀a. σ1 ≲ σ2
G

gpc-cs-arrowΨ `G σ 3 ≲ σ 1

gpc-cs-forallRΨ, a `G σ1 ≲ σ2Ψ `G σ 2 ≲ σ 4

Ψ `G σ 1 → σ 2 ≲ σ 3 → σ 4

Ψ `G σ1 ≲ ∀a. σ2

gpc-cs-unknownL

gpc-cs-unknownRΨ` ?≲σΨ `G σ ≲ ?

GFigure 4.6: Consistent Subtyping for implicit polymorphism.

Here differences of the types in boxes are erased because of the restriction operator. Now if wecompare the types in boxes directly instead of through the lens of the restriction operator, we
can observe that the consistent subtyping relation always holds between the unknown type andan arbitrary type. We can interpret this observation directly from Definition 5: the unknown
type is neutral to subtyping (? <: ?), the unknown type is consistent with any type (? ∼ σ),and subtyping is reflexive (σ <: σ). Therefore, the unknown type is a consistent subtype ofany type (? ≲ σ), and vice versa (σ ≲ ?). Note that this interpretation provides a general
recipe for lifting a (static) subtyping relation to a (gradual) consistent subtyping relation, as
discussed below.
Defining Consistent Subtyping Directly. From the above discussion, we can define
the consistent subtyping relation directly, without resorting to subtyping or consistency atall. The key idea is that we replace <: with ≲ in Figure 4.2, get rid of rule gpc-s-unknown
and add two extra rules concerning ?, resulting in the rules of consistent subtyping in Figure 4.6. Of particular interest are the rules gpc-cs-unknownL and gpc-cs-unknownR,both of which correspond to what we just said: the unknown type is a consistent subtype ofany type, and vice versa.
From now on, we use the symbol ≲ to refer to the consistent subtyping relation in Figure 4.6. What is more, we can prove that the two definitions are equivalent.
Theorem 4.4. Ψ `G σ1 ≲ σ2 ⇔ Ψ `G σ1 <: σ ′ , σ ′ ∼ σ ′′ , Ψ `G σ ′′ <: σ2 for some σ ′ , σ ′′ .

4.3 Gradually Typed Implicit Polymorphism
In Section 4.2 we introduced our consistent subtyping relation that accommodates polymorphic types. In this section we continue with the development by giving a declarative type77

4 Gradually Typed Higher-Rank Polymorphism
system for predicative implicit polymorphism, GPC, that employs the consistent subtyping
relation. The declarative system itself is already quite interesting as it is equipped with both
higher-rank polymorphism and the unknown type.
The syntax of expressions in the declarative system is given at the top of Figure 4.7. The
definition of expressions are the same as of OL in Figure 2.3. Meta-variable e ranges overexpressions. Expressions include variables x, integers n, annotated lambda abstractions λx :σ. e, un-annotated lambda abstractions λx. e, applications e1 e2 , and let expressions let x =e1 in e2 .

4.3.1 Typing in Detail
Figure 4.7 gives the typing rules for our declarative system (the reader is advised to ignore
the gray-shaded parts for now). Rule gpc-var extracts the type of the variable from thetyping context. Rule gpc-int always infers integer types. Rule gpc-lamann puts x with type
annotation σ into the context, and continues type checking the body e. Rule gpc-lam assigns
a monotype τ to x, and continues type checking the body e. Gradual types and polymorphic
types are introduced via explicit annotations. Rule gpc-gen puts a fresh type variable a into
the type context and generalizes the typing result σ to ∀a. σ. Rule gpc-let infers the type
σ of e1 , then puts x : σ in the context to infer the type of e2 . Rule gpc-app first infers thetype of e1 , then the matching judgment Ψ `G σ . σ1 → σ2 extracts the domain type σ1 andthe codomain type σ2 from type σ. The type σ3 of the argument e2 is then compared with σ1
using the consistent subtyping judgment.

Matching. The matching judgment of Siek et al. [2015] is extended to polymorphic types
naturally, resulting in Ψ `G σ . σ1 → σ2 . Note that the matching rules generalize that of DK
in Section 2.3.2 with the unknown type. In rule gpc-m-forall, a monotype τ is guessed
to instantiate the universal quantifier a. If σ is a polymorphic type, the judgment works byguessing instantiations until it reaches an arrow type. Rule gpc-m-arr returns the domain
type σ1 and range type σ2 as expected. If the input is ?, then rule gpc-m-unknown returns? as both the type for the domain and the range.
Note that in GPC, matching saves us from having a subsumption rule (rule ol-sub in
Figure 2.5). The subsumption rule is incompatible with consistent subtyping, since the latter
is not transitive. A discussion of a subsumption rule based on normal subtyping can be foundin Section 4.5.2.

78

4.3 Gradually Typed Implicit Polymorphism

Expressionse::=

x | n | λx : σ. e | λx. e | e1 e2 | let x = e1 in e2

Ψ `G e : σ ⇝ s(Typing)

gpc-vargpc-gen

gpc-int(x : σ) ∈ ΨΨ `G x : σ ⇝ x

Ψ, a `G e : σ ⇝ sΨ `G n : Int ⇝ nΨ `G e : ∀a. σ ⇝ Λa. s

gpc-lamann

gpc-lamΨ `G λx : σ. e : σ → σ2 ⇝ λx : σ. sΨ `G λx. e : τ → σ2 ⇝ λx : τ. s

Ψ, x : σ `G e : σ2 ⇝ s

Ψ, x : τ `G e : σ2 ⇝ s

gpc-letΨ ` G e1 : σ ⇝ s 1

Ψ, x : σ `G e2 : σ2 ⇝ s2

Ψ `G let x = e1 in e2 : σ2 ⇝ (λx : σ. s2 ) s1
gpc-appΨ ` G e1 : σ ⇝ s 1

Ψ `G σ . σ 1 → σ 2

Ψ ` G e2 : σ 3 ⇝ s 2

Ψ `G σ 3 ≲ σ 1

Ψ `G e1 e2 : σ2 ⇝ (hσ ,→ σ1 → σ2 is1 ) (hσ3 ,→ σ1 is2 )Ψ `G σ . σ 1 → σ 2(Matching)gpc-m-forallΨ `G τ

Ψ `G σ[a 7→ τ ] . σ1 → σ2

Ψ `G ∀a. σ . σ1 → σ2

gpc-m-arrΨ `G σ 1 → σ 2 . σ 1 → σ 2

gpc-m-unknownΨ `G ? . ? → ?

Figure 4.7: Syntax of expressions and declarative typing of declarative GPC

79

4 Gradually Typed Higher-Rank Polymorphism

4.3.2 Type-directed Translation
We give the dynamic semantics of our language by translating it to λB [Ahmed et al. 2009].
Below we show a subset of the terms in λB that are used in the translation:
λB Termss::=

x | n | λx : σ. s | Λa. s | s1 s2 | hσ1 ,→ σ2 is

A cast hσ1 ,→ σ2 is converts the value of term s from type σ1 to type σ2 . A cast from σ1
to σ2 is permitted only if the types are compatible, written σ1 ≺ σ2 , as briefly mentioned in
Section 4.2.1. The syntax of types in λB is the same as ours.
The translation is given in the gray-shaded parts in Figure 4.7. The only interesting casehere is to insert explicit casts in the application rule. Note that there is no need to translate
matching or consistent subtyping. Instead we insert the source and target types of a castdirectly in the translated expressions, thanks to the following two lemmas:
Lemma 4.5 (. to ≺). If Ψ `G σ . σ1 → σ2 , then σ ≺ σ1 → σ2 .
Lemma 4.6 (≲ to ≺). If Ψ `G σ1 ≲ σ2 , then σ1 ≺ σ2 .
In order to show the correctness of the translation, we prove that our translation always
produces well-typed expressions in λB. By Lemmas 4.5 and 4.6, we have the following theorem:
Theorem 4.7 (Type Safety). If Ψ `G e : σ ⇝ s , then Ψ `B s : σ.
Parametricity. An important semantic property of polymorphic types is relational parametricity [Reynolds 1983]. The parametricity property says that all instances of a polymorphic function should behave uniformly. A classic example is a function with the type∀a. a → a. The parametricity property guarantees that a value of this type must be either
the identity function (i.e., λx. x) or the undefined function (one which never returns a value).
However, with the addition of the unknown type ?, careful measures are to be taken to ensure
parametricity. Our translation target λB is taken from Ahmed et al. [2009], where relationalparametricity is enforced by dynamic sealing [Matthews and Ahmed 2008; Neis et al. 2009],
but there is no rigorous proof. Later, Ahmed et al. [2009] imposed a syntactic restriction on
terms of λB, where all type abstractions must have values as their body. With this invariant, they proved that the restricted λB satisfies relational parametricity. It remains to see if
our translation process can be adjusted to target restricted λB. One possibility is to impose
similar restriction to the rule gpc-gen:
Ψ, a `G e : σ ⇝ vΨ `G e : ∀a. σ ⇝ Λa. v

80

gpc-gen2

4.3 Gradually Typed Implicit Polymorphism
where we only generate type abstractions if the inner body is a value. However, the typesystem with this rule is a weaker calculus, which is not a conservative extension of the OL
type system.
Ambiguity from Casts. The translation does not always produce a unique target expression. This is because when guessing some monotype τ in rule gpc-m-forall and rule gpccs-forallL, we could have many choices, which inevitably leads to different types. This is
usually not a problem for (non-gradual) System F-like systems [Dunfield and Krishnaswami
2013; Peyton Jones et al. 2007] because they adopt a type-erasure semantics [Pierce 2002].
However, in our case, the choice of monotypes may affect the runtime behaviour of translated
programs, since they could appear inside the explicit casts. For instance, the following example shows two possible translations for the same source expression (λx : ?. f x) : ? → Int,
where the type of f is instantiated to Int → Int and Bool → Int, respectively:f : ∀a. a → Int `G (λx : ?. f x) : ? → Int⇝ (λx : ?. (h∀a. a → Int ,→ Int → Intif) ( h? ,→ Inti x))
f : ∀a. a → Int `G (λx : ?. f x) : ? → Int⇝ (λx : ?. (h∀a. a → Int ,→ Bool → Intif)) ( h? ,→ Booli x))
If we apply λx : ?. f x to 3, which is fine since the function can take any input, the firsttranslation runs smoothly in λB, while the second one will raise a cast error (Int cannot becast to Bool). Similarly, if we apply it to true, then the second succeeds while the first fails.
The culprit lies in the highlighted parts where the instantiation of a appears in the explicit
cast. More generally, any choice introduces an explicit cast to that type in the translation,
which causes a runtime cast error if the function is applied to a value whose type does notmatch the guessed type. Note that this does not compromise the type safety of the translated
expressions, since cast errors are part of the type safety guarantees.
The semantic discrepancy is due to the guessing nature of the declarative system. As far
as the static semantics is concerned, both Int → Int and Bool → Int are equally acceptable.
But this is not the case at runtime. The astute reader may have found that the only appropriate choice is to instantiate the type of f to ? → Int in the matching judgment. However,as specified by rule gpc-m-forall in Figure 4.7, we can only instantiate type variables tomonotypes, but ? is not a monotype! We will get back to this issue in Chapter 5.
Coherence. The ambiguity of translation seems to imply that the declarative system isincoherent. A semantics is coherent if distinct typing derivations of the same typing judgment81

4 Gradually Typed Higher-Rank Polymorphism
possess the same meaning [Reynolds 1991]. We argue that the declarative system is coherent
up to cast errors in the sense that a well-typed program produces a unique value, or results
in a cast error. In the above example, suppose f is defined as (λx. 1), then whatever thetranslation might be, applying (λx : ?. f x) to 3 either results in a cast error, or produces 1,
nothing else.
We defined contextual equivalence [Morris Jr 1969] to formally characterize that two open
expressions have the same behavior. The definition of contextual equivalence requires a notion of well-typed expression contexts C, written C : (Ψ `B σ) ⇝ (Ψ′ `B σ ′ ). The definitions of contexts and context typing are standard and thus omitted. We first define contextual
approximation in a conventional way. In our setting, we need to relax the notion of contextual approximation of λB [Ahmed et al. 2009] to also take into consideration of cast errors.
We write Ψ ` s1 ctx s2 : σ to say that s2 mimics the behaviour of s1 at type σ in the sensethat whenever a program containing s1 reduces to an integer, replacing it with s2 either reduces to the same integer, or emits a cast error. We restrict the program results to integers
to eliminate the role of types in values. If it is not an integer, it is always possible to embed it
into another context that reduces to an integer. Then we write Ψ ` s1 ⋍ctx s2 : σ to say s1and s2 are contextually equivalent, that is, they approximate each other.
Definition 8 (Contextual Approximation and Equivalence up to Cast Errors).
Ψ ` s1 ctx s2 : σ≜Ψ `B s 1 : σ ∧ Ψ `B s 2 : σ ∧for all C. C : (Ψ `B σ) ⇝ (• `B Int) =⇒C{s1 } ⇓ n =⇒ (C{s2 } ⇓ n ∨ C{s2 } ⇓ blame)Ψ ` s1 ⋍ctx s2 : σ≜Ψ ` s1 ctx s2 : σ ∧ Ψ ` s2 ctx s1 : σ

Before presenting the formal definition of coherence, first we observe that after erasing
types and casts, all translations of the same expression are exactly the same. This is easy
to see by examining each elaboration rule. We use bsc to denote an expression in λB after
erasure.
Lemma 4.8. If Ψ `G e : σ ⇝ s1 , and Ψ `G e : σ ⇝ s2 , then bs1 c ≡α bs2 c.
Second, at runtime, the only role of types and casts is to emit cast errors caused by typemismatch. Therefore, By Lemma 4.8 coherence follows as a corollary:
Lemma 4.9 (Coherence up to cast errors). For any expression e such that Ψ `G e : σ ⇝ s1
and Ψ `G e : σ ⇝ s2 , we have Ψ ` s1 ⋍ctx s2 : σ.

82

4.3 Gradually Typed Implicit Polymorphism

4.3.3 Correctness Criteria
Siek et al. [2015] present a set of properties, the refined criteria, that a well-designed gradualtyping calculus must have. Among all the criteria, those related to the static aspects of gradualtyping are well summarized by Cimini and Siek [2016]. Here we review those criteria andadapt them to our notation. We have proved in Coq that our type system satisfies all thesecriteria.
Lemma 4.10 (Correctness Criteria).
• Conservative extension: for all static Ψ, e, and σ1 ,– if Ψ `OL e : σ1 , then there exists σ2 , such that Ψ `G e : σ2 , and Ψ `G σ2 <: σ1 .
– if Ψ `G e : σ, then Ψ `OL e : σ• Monotonicity w.r.t. precision: for all Ψ, e, e′ , σ1 , if Ψ `G e : σ1 , and e′ v e, thenΨ `G e′ : σ2 , and σ2 v σ1 for some σ2 .
• Type Preservation of cast insertion: for all Ψ, e, σ, if Ψ `G e : σ, then Ψ `G e : σ ⇝ s ,and Ψ `B s : σ for some s.
• Monotonicity of cast insertion: for all Ψ, e1 , e2 , s1 , s2 , σ, if Ψ `G e1 : σ ⇝ s1 , andΨ `G e2 : σ ⇝ s2 , and e1 v e2 , then Ψ p Ψ ` s1 vB s2 .
The first criterion states that the gradual type system should be a conservative extensionof the original system. In other words, a static program is typeable in the OL type system if
and only if it is typeable in the gradual type system. A static program is one that does notcontain any type ?6 . However since our gradual type system does not have the subsumption
rule, it produces more general types.
The second criterion states that if a typeable expression loses some type information, it
remains typeable. This criterion depends on the definition of the precision relation, written
σ1 v σ2 , which is given in Figure 4.8. The relation intuitively captures a notion of types containing more or less unknown types (?). The precision relation over types lifts to programs,i.e., e1 v e2 means that e1 and e2 are the same program except that e1 has more unknown
types.
The first two criteria are fundamental to gradual typing. They explain for example whythese two programs λx : Int. x + 1 and λx : ?. x + 1 are typeable, as the former is typeable in
the OL type system and the latter is a less-precise version of it.
6

Note that the term static has appeared several times with different meanings.

83

4 Gradually Typed Higher-Rank Polymorphism

σ1 v σ2(Type Precision)

gpc-l-unknown

gpc-l-int?vσ

Int v Int

gpc-l-arrowσ1 v σ3

gpc-l-tvar

σ2 v σ4

σ1 → σ2 v σ3 → σ4

ava

gpc-l-forall

σ1 v σ2∀a. σ1 v ∀a. σ2
e1 v e2(Term Precision)
gpc-le-lamann

gpc-le-refl

σ1 v σ2eve

gpc-le-app

e1 v e2e1 v e3

λx : σ1 . e1 v λx : σ2 . e2

e1 e2 v e3 e4

s1 v s2(Term Precision in λB)

b-le-varb-le-natxvx

nvn
b-le-apps1 v s3

s2 v s4

s1 s2 v s3 s4

b-le-lamann

σ1 v σ2

s1 v s2

λx : σ1 . s1 v λx : σ2 . s2
b-le-cast

σ1 v σ3

σ2 v σ4b-le-tabs

s1 v s2

Λa. s1 v Λa. s2
s1 v s2

hσ1 ,→ σ2 is1 v hσ3 ,→ σ4 is2

Figure 4.8: Less Precision84

e2 v e4

4.4 Algorithmic Type System
The last two criteria relate the compilation to the cast calculus. The third criterion is essentially the same as Theorem 4.7, given that a target expression should always exist, which
can be easily seen from Figure 4.7. The last criterion ensures that the translation must be
monotonic over the precision relation v. Ahmed et al. [2009] does not include a formal
definition of precision, but an approximation definition and a simulation relation. Here we
adapt the simulation relation as the precision, and a subset of it that is used in our system isgiven at the bottom of Figure 4.8.
The Dynamic Gradual Guarantee. Besides the static criteria, there is also a criterion
concerning the dynamic semantics, known as the dynamic gradual guarantee [Siek et al.
2015].
Definition 9 (Dynamic Gradual Guarantee). Suppose e′ v e, and • `G e : σ ⇝ s and• ` G e′ : σ ′ ⇝ s ′ ,• if s ⇓ v, then s′ ⇓ v ′ and v ′ v v. If s ⇑ then s′ ⇑.
• if s′ ⇓ v ′ , then s ⇓ v where v ′ v v, or s ⇓ blame. If s′ ⇑ then s ⇑ or s ⇓ blame.
The first part of the dynamic gradual guarantee says that if a gradually typed program evaluates to a value, then making type annotations less precise always produces a program that
evaluates to an less precise value. Unfortunately, coherence up to cast errors in the declarative
system breaks the dynamic gradual guarantee. For instance:(λf : ∀a. a → Int. λx : Int. f x) (λx. 1) 3

(λf : ∀a. a → Int. λx : ?. f x) (λx. 1) 3

The left one evaluates to 1, whereas its less precise version (right) will give a cast error if a isinstantiated to Bool for example. In Chapter 5, we will present an extension of the declarative
system that will alleviate the issue.

4.4 Algorithmic Type System
In this section we give a bidirectional account of the algorithmic type system that implements
the declarative specification. The algorithm is largely inspired by the algorithmic bidirectional system of DK [Dunfield and Krishnaswami 2013]. However our algorithmic systemdiffers from theirs in three aspects: (1) the addition of the unknown type ?; (2) the use of thematching judgment; and 3) the approach of gradual inference only producing static types [Garcia and Cimini 2015]. We then prove that our algorithm is both sound and complete withrespect to the declarative type system. We also provide an implementation.

85

4 Gradually Typed Higher-Rank Polymorphism

Expressions
Types
Monotypes
Algorithmic Contexts
Complete ContextseστΓ, ∆, ΘΩx | n | λx : σ. e | λx. e | e1 e2 | e : σ | let x = e1 in e2
Int | a | α
b | σ1 → σ2 | ∀a. σ | ?
Int | a | α
b | τ1 → τ2
• | Γ, x : σ | Γ, a | Γ, αb | Γ, αb = τ | Γ, ▶αb• | Ω, x : σ | Ω, a | Ω, αb = τ | Ω, ▶αb

::=
::=
::=
::=
::=

Γ `G σ(Well-formedness of types)

gpc-ad-int

gpc-ad-unknown

gpc-ad-tvar

gpc-ad-evarΓ `G IntΓ `G ?

Γ[a] `G a

Γ[bα ] `G αb

gpc-ad-arrowgpc-ad-solvedΓ `G σ 1Γ[bα = τ ] `G αb

Γ `G σ 2Γ `G σ 1 → σ 2`G Γ

gpc-ad-forall

Γ, a `G σΓ `G ∀a. σ(Well-formedness of algorithmic contexts)
gpc-wf-var

gpc-wf-empty

`G Γ

` •

x ∈
/ fv (Γ)Γ `G σgpc-wf-tvar`G Γ` Γ, x : σGgpc-wf-evar

`G Γ

α
b∈
/ fv(Γ)gpc-wf-solved`G Γ` Γ, αbαb∈/ fv(Γ)` Γ, α
b=τ

G

G

a ∈
/ fv (Γ)` Γ, aGG

Γ `G τ

gpc-wf-marker

`G Γ

▶αb ∈
/ fv(Γ)` Γ, ▶αbG

Figure 4.9: Syntax and well-formedness of the algorithmic GPC

Algorithmic Contexts. The top of Figure 4.9 shows the syntax of the algorithmic system. A noticeable difference are the algorithmic contexts Γ, which are represented as an ordered list containing declarations of type variables a and term variables x : σ. Unlike declarative contexts, algorithmic contexts also contain declarations of unification type variables
αb, which can be either unsolved (written αb) or solved to some monotype (written αb = τ ).
Finally, algorithmic contexts include a marker ▶αb (read “marker αb” ), which is used to delineate unification variables created by the algorithm. We will have more to say about markers
when we examine the rules. Complete contexts Ω are the same as contexts, except that they
contain no unsolved variables.
Apart from expressions in the declarative system, we add annotated expressions e : σ. The
well-formedness judgments for types and contexts are shown in Figure 4.9.

86

4.4 Algorithmic Type System
Notational convenience. Following DK’s system, we use contexts as substitutions ontypes. We write [Γ]σ to mean Γ applied as a substitution to type σ, whose definition is given
below. We also use a hole notation, which is useful when manipulating contexts by inserting
and replacing declarations in the middle. The hole notation is used extensively in proving
soundness and completeness. For example, Γ[Θ] means Γ has the form ΓL , Θ, ΓR ; if we have
Γ[bα] = (ΓL , αb, ΓR ), then Γ[bα = τ ] = (ΓL , αb = τ, ΓR ). Occasionally, we will see a context
with two ordered holes, e.g., Γ = Γ0 [Θ1 ][Θ2 ] means Γ has the form ΓL , Θ1 , ΓM , Θ2 , ΓR .
[Γ]Int

=

Int

[Γ]a

=

a

[Γ[b
α]]b
α

=

α
b

[Γ[b
α = τ ]]b
α

=

[Γ[b
α = τ ]]τ[Γ]σ1 → σ2=

[Γ]σ1 → [∆]σ2[Γ](∀a. σ)=∀a. [Γ]σ

Input and output contexts. The algorithmic system, compared with the declarative
system, includes similar judgment forms, except that we replace the declarative context Ψwith an algorithmic context Γ (the input context), and add an output context ∆ after a backward turnstile, e.g., Γ `G σ1 ≲ σ2 a ∆ is the judgment form for the algorithmic consistentsubtyping. All algorithmic rules manipulate input and output contexts in a way that is consistent with the notion of context extension, which will be described in Section 4.4.5.
We start with the explanation of the algorithmic consistent subtyping as it involves manipulating unification type variables explicitly (and solving them if possible).

4.4.1 Algorithmic Consistent Subtyping
Figure 4.10 presents the rules of algorithmic consistent subtyping Γ `G σ1 ≲ σ2 a ∆, whichsays that under input context Γ, σ1 is a consistent subtype of σ2 , with output context ∆. The
first five rules do not manipulate contexts, but illustrate how contexts are propagated.
Rule gpc-as-tvar and rule gpc-as-int do not involve unification variables, so the output contexts remain unchanged. Rule gpc-as-evar says that any unsolved unification variable is a consistent subtype of itself. The output is still the same as the input context as
the rule gives no clue as to what is the solution of that unification variable. Rules gpcas-unknownL and as-unknownR are the counterparts of rule gpc-cs-unknownL andrule gpc-cs-unknownR.
Rule gpc-as-arrow is a natural extension of its declarative counterpart. The output context of the first premise is used by the second premise, and the output context of the second87

4 Gradually Typed Higher-Rank PolymorphismΓ `G σ 1 ≲ σ 2 a ∆context ∆)(Under input context Γ, σ1 is a consistent subtype of σ2 , with output

gpc-as-tvar

gpc-as-int

gpc-as-evar

Γ[a] `G a ≲ a a Γ[a]Γ `G Int ≲ Int a Γ

Γ[bα ] `G αb≲αb a Γ[bα]gpc-as-arrowgpc-as-unknownL

gpc-as-unknownRΓ `G ? ≲ σ a ΓΓ `G σ ≲ ? a ΓΓ `G σ 3 ≲ σ 1 a ΘΘ `G [Θ]σ2 ≲ [Θ]σ4 a ∆gpc-as-forallRΓ, a `G σ1 ≲ σ2 a ∆, a, ΘΓ `G σ 1 → σ 2 ≲ σ 3 → σ 4 a ∆gpc-as-forallLΓ, ▶αb , αb `G σ1 [a 7→ αb] ≲ σ2 a ∆, ▶αb , ΘΓ `G σ1 ≲ ∀a. σ2 a ∆gpc-as-instL

αb∈/ fv(σ)Γ `G ∀a. σ1 ≲ σ2 a ∆Γ[bα ] `G αb≲σa∆

gpc-as-instRαb∈/ fv(σ)Γ[bα ] `G σ ⪅ αba∆

Γ[bα ] `G σ ≲ αba∆Figure 4.10: Algorithmic consistent subtyping88

Γ[bα ] `G αb⪅σa∆4.4 Algorithmic Type System
premise is the output context of the conclusion. Note that we do not simply check σ2 ≲ σ4 ,but apply Θ (the input context of the second premise) to both types (e.g., [Θ]σ2 ). This is tomaintain an important invariant: whenever Γ `G σ1 ≲ σ2 a ∆ holds, the types σ1 and σ2
are fully applied under input context Γ (they contain no unification variables already solvedin Γ). The same invariant applies to every algorithmic judgment.
Rule gpc-as-forallR, similar to the declarative rule gpc-cs-forallR, adds a to the input
context. Note that the output context of the premise allows additional unification variables
to appear after the type variable a, in a trailing context Θ. These unification variables could
depend on a; since a goes out of scope in the conclusion, we need to drop them from theconcluding output, resulting in ∆. The next rule is essential to eliminating the guessing work.
Instead of guessing a monotype τ out of thin air, rule gpc-as-forallL generates a fresh
unification variable αb, and replaces a with αb in the body σ. The new unification variable αb isthen added to the input context, just before the marker ▶αb . The output context (∆, ▶αb , Θ)allows additional unification variables to appear after ▶αb in Θ. For the same reasons as inrule gpc-as-forallR, we drop them from the output context. A central idea behind these
two rules is that we defer the decision of picking a monotype for a type variable, and hope thatit could be solved later when we have more information at hand. As a side note, when bothtypes are universal quantifiers, then either rule gpc-as-forallR or rule gpc-as-forallL
applies. In practice, one can apply rule gpc-as-forallR eagerly as it is invertible.
The last two rules (rule gpc-as-instL and rule gpc-as-instR) are specific to the algorithm, thus having no counterparts in the declarative version. They both check consistentsubtyping with an unsolved unification variable on one side and an arbitrary type on theother side. Apart from checking that the unification variable does not occur in the type σ,both rules do not directly solve the unification variables, but leave the real work to the instantiation judgment.

4.4.2 Instantiation
Two symmetric judgments Γ `G αb ⪅ σ a ∆ and Γ `G σ ⪅ αb a ∆, defined in Figure 4.11,
instantiate unsolved unification variables. They read “under input context Γ, instantiate αb toa consistent subtype (or supertype) of σ, with output context ∆”. The judgments are extended
naturally from DK system, whose original inspiration comes from Cardelli [1993]. Since
these two judgments are mutually defined, we discuss them together.
Rule gpc-instl-solve is the simplest one – when an unification variable meets a monotype – where we simply set the solution of αb to the monotype τ in the output context. We
also need to check that the monotype τ is well-formed under the prefix context Γ.

89

4 Gradually Typed Higher-Rank PolymorphismΓ `G αb⪅σa∆context ∆)(Under input context Γ, instantiate αb such that αb ≲ σ, with output

gpc-instl-solve

gpc-instl-solveUΓ `G τΓ, αb , Γ ′ `G αb ⪅ τ a Γ, αb = τ, Γ′Γ[bα ] `G αb ⪅ ? a Γ[bα]
gpc-instl-forallR

gpc-instl-reach

Γ[bα], b `G αb ⪅ σ a ∆, b, Θb `G αΓ[bα][β]b ⪅ βb a Γ[bα][βb = αb]Γ[bα ] `G αb ⪅ ∀b. σ a ∆gpc-instl-arrΓ[bα2 , αb1 , αb=αb1 → αb 2 ] `G σ 1 ⪅ α
b1 a Θ

Θ `G αb2 ⪅ [Θ]σ2 a ∆Γ[bα ] `G αb ⪅ σ1 → σ2 a ∆Γ `G σ ⪅ αba∆context ∆)(Under input context Γ, instantiate αb such that σ ≲ αb, with output

gpc-instr-solve

gpc-instr-solveUΓ `G τΓ, αb , Γ ′ `G τ ⪅ αb a Γ, αb = τ, Γ′

gpc-instr-forallL

gpc-instr-reachb `G βb ⪅ α
Γ[b
α][β]
b a Γ[bα][βb = αb]
gpc-instr-arr

Γ[bα ] `G ? ⪅ αb a Γ[b
α]

b ⪅α
Γ[b
α], ▶βb, βb `G σ[b 7→ β]b a ∆, ▶βb, ΘΓ[bα2 , αb1 , αb=αb1 → αb 2 ] `G αb 1 ⪅ σ1 a ΘΓ[bα] `G ∀b. σ ⪅ αba∆Θ `G [Θ]σ2 ⪅ αb2 a ∆

Γ[bα ] `G σ 1 → σ 2 ⪅ αba∆Figure 4.11: Algorithmic instantiation

904.4 Algorithmic Type System
Rule gpc-instl-solveU is similar to rule gpc-as-unknownR in that we put no constraint7 on αb when it meets the unknown type ?. This design decision reflects the pointthat type inference only produces static types [Garcia and Cimini 2015].
Rule gpc-instl-reach deals with the situation where two unification variables meet. Reb denotes a context where some unsolved unification variable α
call that Γ[bα][β]b is declaredb In this situation, the only logical thing we can do is to set the solution of one unifibefore β.

cation variable to the other one, depending on which one is declared before. For example, inthe output context of rule gpc-instl-reach, we have βb = α
b because in the input context, αbb
is declared before β.

Rule gpc-instl-forallR is the instantiation version of rule gpc-as-forallR. Since oursystem is predicative, αb cannot be instantiated to ∀b. σ, but we can decompose ∀b. σ in thesame way as in rule gpc-as-forallR. Rule gpc-instr-forallL is the instantiation version
of rule gpc-as-forallL.
Rule gpc-instl-arr applies when αb meets an arrow type. It follows that the solution mustalso be an arrow type. This is why, in the first premise, we generate two fresh unification
variables α
b1 and αb2 , and insert them just before αb in the input context, so that we can solve
αb to αb1 → αb2 . Note that the first premise σ1 ⪅ α
b1 switches to the other instantiation
judgment.

4.4.3 Algorithmic Typing
We now turn to the algorithmic typing rules in Figure 4.12. Because general type inference for System F is undecidable [Wells 1999], our algorithmic system uses bidirectional
type checking to accommodate (first-class) polymorphism. Traditionally, two modes are
employed in bidirectional systems: the checking mode Γ `G e ⇐ σ a Θ, which takes aterm e and a type σ as input, and ensures that the term e checks against σ; the inferencemode Γ `G e ⇒ σ a Θ, which takes a term e and produces a type σ. We first discuss rulesin the inference mode.
Rule gpc-inf-var and rule gpc-inf-int do not generate any new information and simply propagate the input context. Rule gpc-inf-anno is standard, switching to the checking
mode in the premise.
In rule gpc-inf-lamann, we generate a fresh unification variable βb for the function codomain,b Note that it is tempting to write Γ, x : σ `G e ⇒and check the function body against β.
σ2 a ∆, x : σ, Θ as the premise (in the hope of better matching its declarative counterpart
rule gpc-lamann), which has a subtle consequence. Consider the expression λx : Int. λy. y.
7

As we will see in Chapter 5 where we present a more refined system, the “no constraint” statement is notentirely true.

91

4 Gradually Typed Higher-Rank PolymorphismΓ `G e ⇒ σ a ∆(Under input context Γ, e infers output type σ, with output context ∆)gpc-inf-var

gpc-inf-anno

gpc-inf-int(x : σ) ∈ ΓΓ `G x ⇒ σ a ΓΓ `G σΓ `G n ⇒ Int a ΓΓ `G e ⇐ σ a ∆Γ `G e : σ ⇒ σ a ∆gpc-inf-lamannΓ `G σb x : σ `G e ⇐ βb a ∆, x : σ, ΘΓ, β,Γ `G λx : σ. e ⇒ σ → βb a ∆gpc-inf-lam

b x:α
Γ, α
b, β,
b `G e ⇐ βb a ∆, x : αb, ΘGbΓ ` λx. e ⇒ αb→βa∆

gpc-inf-let

Γ `G e 1 ⇒ σ a Θ 1

Θ1 , αb , x : σ `G e 2 ⇐ αb a ∆, x : σ, Θ2Γ `G let x = e1 in e2 ⇒ αba∆
gpc-inf-app

Γ `G e 1 ⇒ σ a Θ 1

Θ1 `G [Θ1 ]σ . σ1 → σ2 a Θ2

Θ2 `G e2 ⇐ [Θ2 ]σ1 a ∆Γ `G e 1 e 2 ⇒ σ 2 a ∆Γ `G e ⇐ σ a ∆ (Under input context Γ, e checks against input type σ, with output context∆)
gpc-chk-lam

gpc-chk-gen

Γ, x : σ1 `G e ⇐ σ2 a ∆, x : σ1 , Θ

Γ, a `G e ⇐ σ a ∆, a, ΘΓ `G λx. e ⇐ σ1 → σ2 a ∆gpc-chk-subΓ `G e ⇒ σ 1 a ΘΓ `G e ⇐ ∀a. σ a ∆Θ `G [Θ]σ1 ≲ [Θ]σ2 a ∆Γ `G e ⇐ σ 2 a ∆Γ `G σ . σ 1 → σ 2 a ∆output context ∆)(Under input context Γ, σ matches output type σ1 → σ2 , with

gpc-am-forallΓ, αb `G σ[a 7→ αb ] . σ 1 → σ2 a ∆Γ `G ∀a. σ . σ1 → σ2 a ∆gpc-am-arr

Γ `G σ 1 → σ 2 . σ 1 → σ 2 a Γ

gpc-am-unknown

gpc-am-var

Γ `G ? . ? → ? a ΓΓ[bα ] `G αb.α
b1 → αb2 a Γ[bα1 , αb2 , αb=αb1 → αb2 ]Figure 4.12: Algorithmic typing92

4.4 Algorithmic Type System
Under the new premise, this is untypable because of • `G λx : Int. λy. y ⇒ Int → αb→αba• where α
b is not found in the output context. This explains why we put βb before x : σ so thatit remains in the output context ∆. Rule gpc-inf-lam, which corresponds to rule gpc-lam,one of the guessing rules, is similar to rule gpc-inf-lamann. As with the other algorithmic
rules that eliminate guessing, we create new unification variables αb (for function domain)bb Rule gpc-inf-let isand β (for function codomain) and check the function body against β.
similar to rule gpc-inf-lamann.
Algorithmic Matching. Rule gpc-inf-app deserves attention. It relies on the algorithmic matching judgment Γ `G σ . σ1 → σ2 a ∆. The matching judgment algorithmicallysynthesizes an arrow type from an arbitrary type. Rule gpc-am-forall replaces a with afresh unification variable αb, thus eliminating guessing. Rule gpc-am-arr and rule gpcam-unknown correspond directly to the declarative rules. Rule gpc-am-var, which has
no corresponding declarative version, is similar to rule gpc-instl-arr/gpc-instr-arr: wecreate αb1 and α
b2 and solve α
b to αb1 → αb2 in the output context.
Back to the rule gpc-inf-app. This rule first infers the type of e1 , producing an output
context Θ1 . Then it applies Θ1 to A and goes into the matching judgment, which delivers anarrow type σ1 → σ2 and another output context Θ2 . Θ2 is used as the input context whenchecking e2 against [Θ2 ]σ1 , where we go into the checking mode.
Rules in the checking mode are quite standard. Rule gpc-chk-lam checks against σ1 →σ2 . Rule gpc-chk-gen, like the declarative rule gpc-gen, adds a type variable a to the input
context. Rule gpc-chk-sub uses the algorithmic consistent subtyping judgment.

4.4.4 Decidability
Our algorithmic system is decidable. It is not at all obvious to see why this is the case, as manyrules are not strictly structural (e.g., many rules have [Γ]σ in the premises). This implies
that we need a more sophisticated measure to support the argument. Since the typing rules(Figure 4.12) depend on the consistent subtyping rules (Figure 4.10), which in turn depends
on the instantiation rules (Figure 4.11), to show the decidability of the typing judgment, weneed to show that the instantiation and consistent subtyping judgments are decidable. The
proof strategy mostly follows that of the DK system. Here only highlights of the proofs are
given.
Decidability of Instantiation. The basic idea is that we need to show σ in the instantiation judgments Γ `G αb ⪅ σ a ∆ and Γ `G σ ⪅ αb a ∆ always gets smaller. Most of therules are structural and thus easy to verify (e.g., rule instl-forallR); the non-trivial cases93

4 Gradually Typed Higher-Rank Polymorphism
are rule instl-arr and rule instr-arr where context applications appear in the premises.
The key observation there is that the instantiation rules preserve the size of (substituted)
types. The formal statement of decidability of instantiation needs a few pre-conditions: assuming α
b is unsolved in the input context Γ, that σ is well-formed under the context Γ, that
σ is fully applied under the input context Γ ([Γ]σ = σ), and that αb does not occur in σ.
Those conditions are actually met when instantiation is invoked: rule chk-sub applies the
input context, and the subtyping rules apply input context when needed.
Theorem 4.11 (Decidability of Instantiation). If Γ = Γ0 [bα] and Γ `G σ such that [Γ]σ = σ
and αb∈/ fv(σ) then:
1. Either there exists ∆ such that Γ `G αb ⪅ σ a ∆, or not.
2. Either there exists ∆ such that Γ `G σ ⪅ αb a ∆, or not.
Decidability of Algorithmic Consistent Subtyping. Proving decidability of algorithmic consistent subtyping is a bit more involved, as the induction measure consists ofseveral parts. We measure the judgment Γ `G σ1 ≲ σ2 a ∆ lexicographically by(M1) the number of ∀-quantifiers in σ1 and σ2 ;(M2) the number of unknown types in σ1 and σ2 ;(M3) |unsolved(Γ)|: the number of unsolved unification variables in Γ;(M4) |Γ `G σ1 | + |Γ `G σ2 |.
Notice that because of our gradual setting, we also need to measure the number of unknown
types (M2). This is a key difference from the DK system. For (M4), we use contextual size—
the size of well-formed types under certain contexts, which penalizes solved variables (∗).
Definition 10 (Contextual Size).
|Γ `G Int|=1

|Γ `G ?|=

1|Γ `G a|=1|Γ `G α
b|

=

1

|Γ[b
α = τ ] `G αb|=

1 + |Γ[bα = τ ] `G τ ||Γ `G ∀a. σ|=

1 + |Γ, a `G σ||Γ `G σ=1 + |Γ `G σ1 | + |Γ `G σ2 |1 → σ2 |(∗)

Theorem 4.12 (Decidability of Algorithmic Consistent Subtyping). Given a context Γ andtypes σ1 , σ2 such that Γ `G σ1 and Γ `G σ2 and [Γ]σ1 = σ1 and [Γ]σ2 = σ2 , it is decidable
whether there exists ∆ such that Γ `G σ1 ≲ σ2 a ∆.

94

4.4 Algorithmic Type System
Decidability of Algorithmic Typing. Similar to proving decidability of algorithmic
consistent subtyping, the key is to come up with a correct measure. Since the typing rules
depend on the matching judgment, we first show decidability of the algorithmic matching.
Lemma 4.13 (Decidability of Algorithmic Matching). Given a context Γ and a type σ it isdecidable whether there exist types σ1 , σ2 and a context ∆ such that Γ `G σ . σ1 → σ2 a ∆.
Now we are ready to show decidability of typing. The proof is obtained by induction on
the lexicographically ordered triple: size of e, typing judgment (where the inference mode
⇒ is considered smaller than the checking mode ⇐) and contextual size.
D
e,

⇒
⇐,

|Γ `G σ|E

The above measure is much simpler than the corresponding one in the DK system, where
they also need to consider the application judgment together with the inference and checking
judgments. This shows another benefit (besides the independence from typing) of adopting
the matching judgment.
Theorem 4.14 (Decidability of Algorithmic Typing).
1. Inference: Given a context Γ and a term e, it is decidable whether there exist a type σ
and a context ∆ such that Γ `G e ⇒ σ a ∆.
2. Checking: Given a context Γ, a term e and a type σ such that Γ `G σ, it is decidable
whether there exists a context ∆ such that Γ `G e ⇐ σ a ∆.

4.4.5 Context Extension
To be confident that our algorithmic type system and the declarative type system agree witheach other, we need to prove that the algorithmic rules are sound and complete with respect
to the declarative specification. Before we give the formal statements of the soundness andcompleteness theorems, we need a meta-theoretical device, called context extension [Dunfield and Krishnaswami 2013], to capture a notion of information increase from input contexts to output contexts.
A context extension judgment Γ −→ ∆ reads “Γ is extended by ∆”. Intuitively, this judgment says that ∆ has at least as much information as Γ: some unsolved unification variablesin Γ may be solved in ∆. The full inductive definition can be found Figure 4.13.

95

4 Gradually Typed Higher-Rank PolymorphismΓ −→ ∆
gpc-ext-id• −→ •(Context extension)
gpc-ext-var
Γ −→ ∆[∆]σ = [∆]σ ′Γ, x : σ −→ ∆, x : σ ′gpc-ext-solvedΓ −→ ∆[∆]τ = [∆]τ ′Γ, αb = τ −→ ∆, αb = τ′
gpc-ext-addSolveΓ −→ ∆
Γ −→ ∆, αb=τ

gpc-ext-tvar
Γ −→ ∆
Γ, a −→ ∆, a

gpc-ext-solveΓ −→ ∆
Γ, αb −→ ∆, αb=τ

gpc-ext-evarΓ −→ ∆
Γ, αb −→ ∆, αb
gpc-ext-add
Γ −→ ∆
Γ −→ ∆, αb

gpc-ext-marker
Γ −→ ∆Γ, ▶αb −→ ∆, ▶αbFigure 4.13: Context extension

4.4.6 Soundness
Roughly speaking, soundness of the algorithmic system says that given a derivation of analgorithmic judgment with input context Γ, output context ∆, and a complete context Ω that
extends ∆, applying Ω throughout the given algorithmic judgment should yield a derivabledeclarative judgment. For example, let us consider an algorithmic typing judgment • `G
λx. x ⇒ αb→αbaαb, and any complete context, say, Ω = (bα = Int), then applying Ω to theabove judgment yields • `G λx. x : Int → Int, which is derivable in the declarative system.
However there is one complication: applying Ω to the algorithmic expression does notnecessarily yield a typable declarative expression. For example, by rule gpc-chk-lam wehave λx. x ⇐ (∀a. a → a) → (∀a. a → a), but λx. x itself cannot have type (∀a. a →a) → (∀a. a → a) in the declarative system. To circumvent that, we add an annotation to
the lambda abstraction, resulting in λx : (∀a. a → a). x, which is typeable in the declarative
system with the same type. To relate λx. x and λx : (∀a. a → a). x, we erase all annotations
on both expressions.
Definition 11 (Type annotation erasure). The erasure function is denoted as | · |, and defined
as follows:|x| = x|n| = n|λx : σ. e| = λx. |e||λx. e| = λx. |e||e1 e2 | = |e1 | |e2 ||e : σ| = |e|Theorem 4.15 (Instantiation Soundness). Given ∆ −→ Ω and [Γ]σ = σ and αb∈/ fv(σ):1. If Γ `G αb ⪅ σ a ∆ then [Ω]∆ `G [Ω]bα ≲ [Ω]σ.

96

4.4 Algorithmic Type System
2. If Γ `G σ ⪅ αb a ∆ then [Ω]∆ `G [Ω]σ ≲ [Ω]bα.
Notice that the declarative judgment uses [Ω]∆, an operation that applies a complete context Ω to the algorithmic context ∆, essentially plugging in all known solutions and removing
all declarations of unification variables (both solved and unsolved), resulting in a declarative
context.
With instantiation soundness, next we show that the algorithmic consistent subtyping issound:
Theorem 4.16 (Soundness of Algorithmic Consistent Subtyping). If Γ `G σ1 ≲ σ2 a ∆where [Γ]σ1 = σ1 and [Γ]σ2 = σ2 and ∆ −→ Ω then [Ω]∆ `G [Ω]σ1 ≲ [Ω]σ2 .
Finally the soundness theorem of algorithmic typing is:
Theorem 4.17 (Soundness of Algorithmic Typing). Given ∆ −→ Ω:
1. If Γ `G e ⇒ σ a ∆ then ∃e′ such that [Ω]∆ `G e′ : [Ω]σ and |e| = |e′ |.
2. If Γ `G e ⇐ σ a ∆ then ∃e′ such that [Ω]∆ `G e′ : [Ω]σ and |e| = |e′ |.

4.4.7 CompletenessCompleteness of the algorithmic system is the reverse of soundness: given a declarative judgment of the form [Ω]Γ `G [Ω] . . . , we want to get an algorithmic derivation of Γ `G · · · a ∆.
It turns out that completeness is a bit trickier to state in that the algorithmic rules generateunification variables on the fly, so ∆ could contain unsolved unification variables that are
not found in Γ, nor in Ω. Therefore the completeness proof must produce another complete
context Ω′ that extends both the output context ∆, and the given complete context Ω. As
with soundness, we need erasure to relate both expressions.
Theorem 4.18 (Instantiation Completeness). Given Γ −→ Ω and σ = [Γ]σ and αb ∈unsolved(Γ) and αb∈/ fv(σ):1. If [Ω]Γ `G [Ω]bα ≲ [Ω]σ then there are ∆, Ω′ such that Ω −→ Ω′ and ∆ −→ Ω′ andΓ `G αb ⪅ σ a ∆.
2. If [Ω]Γ `G [Ω]σ ≲ [Ω]b
α then there are ∆, Ω′ such that Ω −→ Ω′ and ∆ −→ Ω′ andΓ `G σ ⪅ αb a ∆.
Next is the completeness of consistent subtyping:97

4 Gradually Typed Higher-Rank Polymorphism
Theorem 4.19 (Generalized Completeness of Consistent Subtyping). If Γ −→ Ω and Γ `G
σ1 and Γ `G σ2 and [Ω]Γ `G [Ω]σ1 ≲ [Ω]σ2 then there exist ∆ and Ω′ such that ∆ −→ Ω′and Ω −→ Ω′ and Γ `G [Γ]σ1 ≲ [Γ]σ2 a ∆.
We prove that the algorithmic matching is complete with respect to the declarative matching:
Theorem 4.20 (Matching Completeness). Given Γ −→ Ω and Γ `G σ, if [Ω]Γ `G [Ω]σ .
σ1 → σ2 then there exist ∆, Ω′ , σ1′ and σ2′ such that Γ `G [Γ]σ . σ1′ → σ2′ a ∆ and ∆ −→ Ω′and Ω −→ Ω′ and σ1 = [Ω′ ]σ1′ and σ2 = [Ω′ ]σ2′ .

Finally here is the completeness theorem of the algorithmic typing:
Theorem 4.21 (Completeness of Algorithmic Typing). Given Γ −→ Ω and Γ `G σ, if[Ω]Γ `G e : σ then there exist ∆, Ω′ , σ ′ and e′ such that ∆ −→ Ω′ and Ω −→ Ω′ andΓ `G e′ ⇒ σ ′ a ∆ and σ = [Ω′ ]σ ′ and |e| = |e′ |.

4.5 Simple Extensions and Variants
This section considers two simple variations of the presented system. The first variation extends the system with a top type, while the second variation considers a more declarative
formulation using a subsumption rule.

4.5.1 Top Types
We argued that our definition of consistent subtyping (Definition 5) generalizes the original
definition by Siek and Taha [2007]. We have shown its applicability to polymorphic types,
for which Siek and Taha [2007] approach cannot be extended naturally. To strengthen ourargument, we show how to extend our approach to > types, which is also not supported by
Siek and Taha [2007] approach.
Consistent Subtyping with >. In order to preserve the orthogonality between subtyping and consistency, we require > to be a common supertype of all static types, as shownin rule gpc-s-top. This rule might seem strange at first glance, since even if we remove therequirement σ static, the rule still seems reasonable. However, an important point is that,
because of the orthogonality between subtyping and consistency, subtyping itself should notcontain a potential information loss! Therefore, subtyping instances such as ? <: > arenot allowed. For consistency, we add the rule that > is consistent with >, which is actually included in the original reflexive rule σ ∼ σ. For consistent subtyping, every type is aconsistent subtype of >, for example, Int → ? ≲ >.

98

4.5 Simple Extensions and Variants

σ staticΨ `G σ <: >gpc-s-top>∼>Ψ `G σ ≲ >gpc-cs-top

It is easy to verify that Definition 5 is still equivalent to that in Figure 4.6 extended withrule gpc-cs-top. That is, Theorem 4.4 holds:
Proposition 4.22 (Extension with >). Ψ `G σ1 ≲ σ2 ⇔ Ψ `G σ1 <: σ ′ , σ ′ ∼ σ ′′ ,Ψ `G σ ′′ <: σ2 for some σ ′ , σ ′′ .
We extend the definition of concretization (Definition 6) with > by adding another equation γ(>) = {>}. Note that Castagna and Lanvin [2017] also have this equation in theircalculus. It is easy to verify that Corollary 4.2 still holds:
f: σ2 .
Proposition 4.23 (Equivalence to AGT on >). σ1 ≲ σ2 if and only if σ1 <Siek and Taha’s Definition of Consistent Subtyping Does Not Work for >.

As

with the analysis in Section 4.2.2, Int → ? ≲ > only holds when we first apply consistency,
then subtyping. However we cannot find a type σ such that Int → ? <: σ and σ ∼ >. The
following diagram depicts the situation:
∅

∼

<:

<:
Int → ?

>∼

Int → Int

Additionally we have a similar problem in extending the restriction operator: non-structural
masking between Int → ? and > cannot be easily achieved.
Note that both the top and universally quantified types can be seen as special cases of intersection types. Indeed, top is the intersection of the empty set, while a universally quantified
type is the intersection of the infinite set of its instantiations [Davies and Pfenning 2000].
Recall from Section 4.2.3 that Castagna and Lanvin [2017] shows that consistent subtyping
from AGT works well for intersection types, and our definition coincides with AGT (Corollary 4.2 and Proposition 4.23). We thus believe that our definition is compatible with conventional binary intersection types as well. Yet, a rigorous formalization would be needed to
substantiate this belief.

99

4 Gradually Typed Higher-Rank Polymorphism

4.5.2 A More Declarative Type System
In Section 4.3 we present our declarative system in terms of the matching and consistentsubtyping judgments. The rationale behind this design choice is that the resulting declarative system combines subtyping and type consistency in the application rule, thus making
it easier to design an algorithmic system accordingly. Still, one may wonder if it is possible
to design a more declarative specification. For example, even though we mentioned that thesubsumption rule is incompatible with consistent subtyping, it might be possible to accommodate a subsumption rule for normal subtyping (instead of consistent subtyping). In thissection, we discuss an alternative for the design of the declarative system.
Wrong Design. A naive design that does not work is to replace rule gpc-app in Figure 4.7
with the following two rules:
gpc-v-app1

gpc-v-subΨ` e:σGΨ `G e1 : σσ <: σ2Ψ ` G e2 : σ 1Ψ `G e : σ 2σ ∼ σ1 → σ2

Ψ `G e1 e2 : σ2Rule gpc-v-sub is the standard subsumption rule: if an expression e has type σ, then it
can be assigned some type σ2 that is a supertype of σ. Rule gpc-v-app1 first infers that e1
has type σ, and e2 has type σ1 , then it finds some σ2 so that σ is consistent with σ1 → σ2 .
There would be two obvious benefits of this variant if it did work: firstly this approachclosely resembles the traditional declarative type systems for calculi with subtyping; secondly
it saves us from discussing various forms of σ in rule gpc-v-app1, leaving the job to theconsistency judgment.
The design is wrong because of the information loss caused by the choice of σ2 in rule gpcv-app1. Suppose we have Ψ `G plus : Int → Int → Int, then we can apply it to 1 to getΨ `G plus : Int → Int → IntΨ `G 1 : Int

Int → Int → Int ∼ Int → ? → IntΨ ` plus 1 : ? → Int

gpc-v-app1

Further applying it to true we getΨ `G plus 1 : ? → IntΨ `G true : BoolΨ ` plus 1 true : Int

100? → Int ∼ Bool → Int

gpc-v-app1

4.5 Simple Extensions and Variants
which is obviously wrong! The type consistency in rule gpc-v-app1 causes information loss
for both the argument type σ1 and the return type σ2 . The problem is that information ofσ2 can get lost again if it appears in further applications. The moral of the story is that weshould be very careful when using type consistency. We hypothesize that it is inevitable to
do case analysis for the type of the function in an application (i.e., σ in rule gpc-v-app1).
Proper Declarative Design. The proper design refines the first variant by using a matching judgment to carefully distinguish two cases for the typing result of e1 in rule gpc-v-app1:(1) when it is an arrow type, and (2) when it is an unknown type. This variant replaces
rule gpc-app in Figure 4.7 with the following rules:
gpc-v-subΨ `G e : σσ <: σ2Ψ `G e : σ 2
gpc-v-app2

Ψ `G e : σΨ `G σ . σ 1 → σ 2

Ψ ` G e2 : σ 3σ1 ∼ σ3Ψ `G e1 e2 : σ2Ψ `G σ 1 → σ 2 . σ 1 → σ 2

Ψ `G ? . ? → ?

Rule gpc-v-sub is the same as in the first variant. In rule gpc-v-app2, we infer that e1
has type σ, and use the matching judgment to get an arrow type σ1 → σ2 . Then we need toensure that the argument type σ3 is consistent with (rather than a consistent subtype of) σ1 ,and use σ2 as the result type of the application. The matching judgment only deals with twocases, as polymorphic types are handled by rule gpc-v-sub. These rules are closely related
to the ones in Siek and Taha [2006] and Siek and Taha [2007].
The more declarative nature of this system also implies that it is highly non-syntax-directed,
and it does not offer any insight into combining subtyping and consistency. We have provedin Coq the following lemmas to establish soundness and completeness of this system withrespect to our original system (to avoid ambiguity, we use the notation `Gm to indicate themore declarative version):
Lemma 4.24 (Completeness of `Gm ). If Ψ `G e : σ, then Ψ `Gm e : σ.
Lemma 4.25 (Soundness of `Gm ). If Ψ `Gm e : σ1 , then there exists some σ2 , such that Ψ `Ge : σ2 and Ψ `G σ2 <: σ1 .

101

5 Restoring the Dynamic Gradual
Guarantee with Type Parameters
In Section 4.3.2 we have seen an example where a single source expression could produce
two different target expressions with different runtime behaviors. As we explained, this isdue to the guessing nature of the declarative system, and, from the (source) typing point ofview, no guessed type is particularly better than any other. As a consequence, this breaks the
dynamic gradual guarantee as discussed in Section 4.3.3.
To alleviate this situation, we introduce static type parameters, which are placeholders formonotypes, and gradual type parameters, which are placeholders for monotypes that are consistent with the unknown type. The concept of static type parameters and gradual type parameters in the context of gradual typing was first introduced by Garcia and Cimini [2015],
and later played a central role in the work of Igarashi et al. [2017]. In our type system, typeparameters mainly help capture the notion of representative translations, and should not appear in a source program. With them we are able to recast the dynamic gradual guarantee
in terms of representative translations, and to prove that every well-typed source expression possesses at least one representative translation. With a coherence conjecture regarding
representative translations, the dynamic gradual guarantee of our extended source language
now can be reduced to that of λB.

5.1 Declarative Type System
The new syntax of types is given at the top of Figure 5.1, with the differences highlighted.
In addition to the types of Figure 4.2, we add static type parameters S, and gradual type parameters G. Both kinds of type parameters are monotypes. The addition of type parameters,
however, leads to two new syntactic categories of types. Castable types C represent types thatcan be cast from or to ?. It includes all types, except those that contain static type parameters.
Castable monotypes t are those castable types that are also monotypes.
Consistent Subtyping. The new definition of consistent subtyping is given at the bottomof Figure 5.1, again with the differences highlighted. Now the unknown type is only a con-103

5 Restoring the Dynamic Gradual Guarantee with Type Parameters

Types

σ

::=

Int | a | σ1 → σ2 | ∀a. σ | ? | S | G

Monotypes

τ::=Int | a | τ1 → τ2 | S | G

Castable Types

C::=

Int | a | C1 → C2 | ∀a. C | ? | G

Castable Monotypest::=

Int | a | t1 → t2 | GΨ `G σ 1 ≲ σ 2(Consistent Subtyping)

gpc-cs-tvara∈Ψ

gpc-cs-int

Ψ `G a ≲ aΨ `G Int ≲ Int

gpc-cs-arrowΨ `G σ 3 ≲ σ 1

Ψ `G σ 2 ≲ σ 4

Ψ `G σ 1 → σ 2 ≲ σ 3 → σ 4

gpc-cs-forallRΨ, a `G σ1 ≲ σ2

gpc-cs-forallLΨ `G τ
Ψ `G σ1 [a 7→ τ ] ≲ σ2Ψ `G σ1 ≲ ∀a. σ2Ψ `G ∀a. σ1 ≲ σ2

gpc-cs-unknownLLΨ `G ? ≲ C

gpc-cs-unknownRR

gpc-cs-spar

gpc-cs-gpar

Ψ `G C ≲ ?

Ψ `G S ≲ S

Ψ `G G ≲ G

Figure 5.1: Syntax of types, and consistent subtyping in the extended declarative system.

sistent subtype of all castable types, rather than of all types (rule gpc-cs-unknownLL), andvice versa (rule gpc-cs-unknownRR). Moreover, the static type parameter S is a consistentsubtype of itself (rule gpc-cs-spar), and similarly for the gradual type parameter (rule gpccs-gpar). From this definition it follows immediately that ? is incomparable with types that
contain static type parameters S, such as S → Int.
Typing and Translation. Given these extensions to types and consistent subtyping, thetyping process remains the same as in Figure 4.7. To account for the changes in the translation, if we extend λB with type parameters as in Garcia and Cimini [2015], then the translation remains the same as well.

5.2 Substitutions and Representative Translations
As we mentioned, type parameters serve as placeholders for monotypes. As a consequence,
wherever a type parameter is used, any suitable monotype could appear just as well. To formalize this observation, we define substitutions for type parameters as follows:
Definition 12 (Substitution). Substitutions for type parameters are defined as:104

5.2 Substitutions and Representative Translations
1. Let S S : S → τ be a total function mapping static type parameters to monotypes.
2. Let S G : G → t be a total function mapping gradual type parameters to castable
monotypes.
3. Let S P = S G ∪ S S be a union of S S and S G mapping static and gradual type parameters accordingly.
Note that since G might be compared with ?, only castable monotypes are suitable substitutes,
whereas S can be replaced by any monotypes. Therefore, we can substitute G for S, but not
the other way around.
Let us go back to our example and its two translations in Section 4.3.2. The problem withthose translations is that neither Int → Int nor Bool → Int is general enough. With typeparameters, however, we can state a more general translation that covers both through substitution:f : ∀a. a → Int `G (λx : ?. f x) : ? → Int⇝ (λx : ?. (h∀a. a → Int ,→ G → Intif)( h? ,→ Gi x))
The advantage of type parameters is that they help reasoning about the dynamic semantics.
Now we are not limited to a particular choice, such as Int → Int or Bool → Int, which mightor might not emit a cast error at runtime. Instead we have a general choice G → Int.
What does the more general choice with type parameters tell us? First, we know that inthis case, there is no concrete constraint on a, so we can instantiate it with a type parameter. Second, the fact that the general choice uses G rather than S indicates that any chosen
instantiation needs to be a castable type. It follows that any concrete instantiation will havean impact on the runtime behavior; therefore it is best to instantiate a with ?. However, type
inference cannot instantiate a with ?, and substitution cannot replace G with ? either. Thismeans that we need a syntactic refinement process of the translated programs in order toreplace type parameters with allowed gradual types.
Syntactic Refinement. We define syntactic refinement of the translated expressions asfollows. As S denotes no constraints at all, substituting it with any monotype would work.
Here we arbitrarily use Int. We interpret G as ? since any monotype could possibly lead to acast error.
Definition 13 (Syntactic Refinement). The syntactic refinement of a translated expression s
is denoted by dse, and defined as follows:105

5 Restoring the Dynamic Gradual Guarantee with Type Parameters

dInte

=

Int

dae

=

a

dσ1 → σ2 e=

dσ1 e → dσ2 ed∀a. σe=∀a. dσe

d?e

=

?

dSe

=

Int

dGe

=

?

Applying the syntactic refinement to the translated expression, we get(λx : ?.(h∀a. a → Int ,→ ? → Inti f)(h? ,→ ? i x))
where two G are refined by ? as highlighted. It is easy to verify that both applying this expression to 3 and to true now results in a translation that evaluates to a value.
Representative Translations. To decide whether one translation is more general thanthe other, we define a preorder between translations.
Definition 14 (Translation Pre-order). Suppose Ψ `G e : σ ⇝ s1 and Ψ `G e : σ ⇝ s2 ,we define s1 ⩽ s2 to mean s2 ≡α S P (s1 ) for some S P .
Proposition 5.1. If s1 ⩽ s2 and s2 ⩽ s1 , then s1 and s2 are α-equivalent (i.e., equivalent up
to renaming of type parameters).
The preorder between translations gives rise to a notion of what we call representativetranslations:Definition 15 (Representative Translation). A translation s is said to be a representativetranslation of a typing derivation Ψ `G e : σ ⇝ s if and only if for any other translationΨ `G e : σ ⇝ s′ such that s′ ⩽ s, we have s ⩽ s′ . From now on we use r to denote a
representative translation.
An important property of representative translations, which we conjecture for the lackof rigorous proof, is that if there exists any translation of an expression that (after syntactic refinement) can reduce to a value, so can a representative translation of that expression.
Conversely, if a representative translation runs into a blame, then no translation of that expression can reduce to a value.
Conjecture 5.2 (Property of Representative Translations). For any expression e such that
Ψ `G e : σ ⇝ s and Ψ `G e : σ ⇝ r and ∀C. C : (Ψ `B σ) ⇝ (• `B Int), we have106

5.3 Dynamic Gradual Guarantee, Reloaded• If C{dse} ⇓ n, then C{dre} ⇓ n.
• If C{dre} ⇓ blame, then C{dse} ⇓ blame.
Given this conjecture, we can state a stricter coherence property (without the “up to casts”part) between any two representative translations. We first strengthen Definition 8 following
Ahmed et al. [2009]:
Definition 16 (Contextual Approximation à la Ahmed et al. [2009]).
Ψ ` s1 ctx s2 : σ≜Ψ `B s 1 : σ ∧ Ψ ` B s 2 : σ ∧for all C. C : (Ψ `B σ) ⇝ (• `B Int) =⇒(C{ds1 e} ⇓ n =⇒ C{ds2 e} ⇓ n) ∧(C{ds1 e} ⇓ blame =⇒ C{ds2 e} ⇓ blame)

The only difference is that now when a program containing s1 reduces to a value, so does
one containing s2 .
From Conjecture 5.2, it follows that coherence holds between two representative translations of the same expression.
Corollary 5.3 (Coherence for Representative Translations). For any expression e such that
Ψ `G e : σ ⇝ r1 and Ψ `G e : σ ⇝ r2 , we have Ψ ` r1 ⋍ctx r2 : σ.
We have proved that for every typing derivation, at least one representative translation
exists.
Lemma 5.4 (Representative Translation for Typing). For any typing derivation Ψ `G e : σthere exists at least one representative translation r such that Ψ `G e : σ ⇝ r .
For our example, (λx : ?. (h∀a. a → Int ,→ G → Intif) (h? ,→ Gix)) is a representativetranslation, while the other two are not.

5.3 Dynamic Gradual Guarantee, Reloaded
Given the above propositions, we are ready to revisit the dynamic gradual guarantee. The
nice thing about representative translations is that the dynamic gradual guarantee of oursource language is essentially that of λB, our target language. However, the dynamic gradualguarantee for λB is still an open question. According to Igarashi et al. [2017], the difficultylies in the definition of term precision that preserves the semantics. We leave it here as aconjecture as well. From a declarative point of view, we cannot prevent the system frompicking undesirable instantiations, but we know that some choices are better than the others,
so we can restrict the discussion of dynamic gradual guarantee to representative translations.

107

5 Restoring the Dynamic Gradual Guarantee with Type Parameters

Types

σ

::=

Int | a | α
b | σ1 → σ2 | ∀a. σ | ? | S | G

Monotypes

τ::=Int | a | α
b | τ1 → τ2 | S | G

Existential variables
Castable Types

α
b
C

::=
::=

α
bS | αbG
Int | a | α
b | C1 → C2 | ∀a. C | ? | G

Castable Monotypest::=

Int | a | α
b | t1 → t2 | G

Algorithmic Contexts

Γ, ∆, Θ::=• | Γ, x : σ | Γ, a | Γ, αb | Γ, αbS = τ | Γ, αbG = t | Γ, ▶αbΩ::=bS = τ | Ω, α
• | Ω, x : σ | Ω, a | Ω, αbG = t | Ω, ▶αbComplete Contexts

Figure 5.2: Syntax of types, contexts and consistent subtyping in the extended algorithmic system.

Conjecture 5.5 (Dynamic Gradual Guarantee in terms of Representative Translations). Suppose e′ v e,
1. If • `G e : σ ⇝ r , dre ⇓ v, then for some σ2 and r′ , we have • `G e′ : σ2 ⇝ r′ , andσ2 v σ, and dr′ e ⇓ v ′ , and v ′ v v.
2. If • `G e′ : σ2 ⇝ r′ , dr′ e ⇓ v ′ , then for some σ and r, we have • `G e : σ ⇝ r , andσ2 v σ. Moreover, dre ⇓ v and v ′ v v, or dre ⇓ blame.
For the example in Section 4.3.3, now we know that the representative translation of theright one will evaluate to 1 as well.
(λf : ∀a. a → Int. λx : Int. f x) (λx : Int. 1) 3

(λf : ∀a. a → Int. λx : Int. f x) (λx : ?. 1) 3

More importantly, in what follows, we show that our extended algorithm is able to findthose representative translations.

5.4 Extended Algorithmic Type System
To understand the design choices involved in the new algorithmic system, we consider thefollowing algorithmic typing example:
f : ∀a. a → Int, x : ? `G f x : Int a f : ∀a. a → Int, x : ?, αbCompared with declarative typing, where we have many choices (e.g., Int → Int, Bool → Int,and so on) to instantiate ∀a. a → Int, the algorithm computes the instantiation α
b → Int withαb unsolved in the output context. What can we know from the algorithmic typing? First weknow that, here αb is not constrained by the typing problem. Second, and more importantly,

1085.4 Extended Algorithmic Type Systemαb has been compared with an unknown type (when typing (f x)). Therefore, it is possible to
make a more refined distinction between different kinds of existential variables:• the first kind of existential variables are those that indeed have no constraints at all, as
they do not affect the dynamic semantics;• the second kind (as in this example) are those where the only constraint is that thevariable was once compared with an unknown type [Garcia and Cimini 2015].
The syntax of types is shown in Figure 5.2. A notable difference, apart from the additionof static and gradual parameters, is that we further split existential variables αb into static
existential variables αbS and gradual existential variables αbG . Depending on whether an existential variable has been compared with ? or not, its solution space changes. More specifically, static existential variables can be solved to a monotype τ , whereas gradual existential
variables can only be solved to a castable monotype t, as can be seen in the changes of algorithmic contexts and complete contexts. As a result, the typing result for the above example
now becomesf : ∀a. a → Int, x : ? `G f x : Int a f : ∀a. a → Int, x : ?, αbGsince we can solve any unconstrained αbG to G, it is easy to verify that the resulting translation
is indeed a representative translation.
Our extended algorithm is novel in the following aspects. We naturally extend the conceptof existential variables [Dunfield and Krishnaswami 2013] to deal with comparisons between
existential variables and unknown types. Unlike Garcia and Cimini [2015], where they usean extra set to store types that have been compared with unknown types, our two kinds ofexistential variables emphasize the type distinction better, and correspond more closely tothe two kinds of type parameters, as we can solve αbS to S and αbG to G.

5.4.1 Extended Algorithmic Consistent Subtyping
While the changes in the syntax seem negligible, the addition of static and gradual type parameters changes the algorithmic judgments in a significant way. We first discuss the algorithmic consistent subtyping, which is shown in Figure 5.3. For notational convenience,
when static and gradual existential variables have the same rule form, we compress them into
one rule. For example, rule gpc-as-evar is really two rules Γ[bα S ] `G αbS ≲ αbS a Γ[bαS ]and Γ[bα G ] `G αbG ≲ αbG a Γ[bαG ]; same for rules gpc-as-instL and gpc-as-instR.

109

5 Restoring the Dynamic Gradual Guarantee with Type ParametersΓ `G σ 1 ≲ σ 2 a ∆(Algorithmic Consistent Subtyping)

gpc-as-tvar

gpc-as-int

gpc-as-evar

Γ[a] `G a ≲ a a Γ[a]Γ `G Int ≲ Int a Γ

Γ[bα ] `G αb≲αb a Γ[bα]gpc-as-spar

gpc-as-gpar

Γ `G S ≲ S a ΓΓ `G G ≲ G a Γ

gpc-as-unknownLL

gpc-as-unknownRRΓ ` ? ≲ C a contaminate(Γ, C)Γ `G C ≲ ? a contaminate(Γ, C)G

gpc-as-arrowΓ `G σ 3 ≲ σ 1 a ΘΘ `G [Θ]σ2 ≲ [Θ]σ4 a ∆Γ `G σ 1 → σ 2 ≲ σ 3 → σ 4 a ∆gpc-as-forallRΓ, a `G σ1 ≲ σ2 a ∆, a, ΘΓ `G σ1 ≲ ∀a. σ2 a ∆gpc-as-forallLLΓ, ▶baS , αbS `G σ1 [a 7→ αbS ] ≲ σ2 a ∆, ▶baS , ΘΓ `G ∀a. σ1 ≲ σ2 a ∆gpc-as-instL
αb∈/ fv(σ)Γ[bα ] `G αb⪅σa∆

gpc-as-instRαb∈/ fv(σ)Γ[bα ] `G σ ⪅ αba∆

Γ[bα ] `G αb≲σa∆Γ[bα ] `G σ ≲ αba∆Figure 5.3: Extended algorithmic consistent subtyping

110

5.4 Extended Algorithmic Type System
Rules gpc-as-spar and gpc-as-gpar are direct analogies of rules gpc-cs-spar and gpccs-gpar. Though looking simple, rules gpc-as-unknownLL and gpc-as-unknownRR deserve much explanation. To understand what the output context contaminate(Γ, C) is for,let us first see why this seemingly intuitive rule Γ `G ? ≲ C a Γ (like rule gpc-asb S `G ? ≲unknownL in the original algorithmic system) is wrong. Consider the judgment α
αbS → αbS a αbS , which seems fine. If this holds, then – since α
bS is unsolved in the output
context – we can solve it to S for example (recall that αbS can be solved to some monotype),
resulting in ? ≲ S → S. However, this is in direct conflict with rule gpc-cs-unknownLLin the declarative system precisely because S → S is not a castable type! A possible solution
would be to transform all static existential variables to gradual existential variables within
C whenever it is being compared to ?: while αb S `G ? ≲ αbS → αbS a αbS does not hold,αb G `G ? ≲ αbG → αbG a αbG does. While substituting static existential variables with gradual existential variables seems to be intuitively correct, it is rather hard to formulate—not
only do we need to perform substitution in C, we also need to substitute accordingly in boththe input and output contexts in order to ensure that no existential variables become unbound. However, making such changes is at odds with the interpretation of input contexts:
they are “input”, which evolve into output contexts with more variables solved. Therefore, inline with the use of input contexts, a simple solution is to generate a new gradual existential
variable and solve the static existential variable to it in the output context, without touching
C at all. So we have α
b S `G ? ≲ αbS → αbS a αbG , αbS = αbG .
Based on the above discussion, the following defines contaminate(Γ, σ):Definition 17. contaminate(Γ, σ) is defined inductively as follows
contaminate(•, σ)=•contaminate((Γ, x : σ), σ)=contaminate(Γ, σ), x : σcontaminate((Γ, a), σ)=contaminate(Γ, σ), acontaminate((Γ, αbS ), σ)=contaminate(Γ, αbG , σ), αbS = αbGif αbS occurs in σ

contaminate((Γ, αbS ), σ)=contaminate(Γ, σ), αbSif αbS does not occur in σ

contaminate((Γ, αbG ), σ)=contaminate(Γ, σ), αbGcontaminate((Γ, αb = τ ), σ)=contaminate(Γ, σ), αb=τcontaminate((Γ, ▶αb ), σ)=contaminate(Γ, σ), ▶αbcontaminate(Γ, σ) solves all static existential variables found within σ to fresh gradual existential variables in Γ. Notice the case for contaminate((Γ, αbS ), σ) is exactly what we havejust described.

111

5 Restoring the Dynamic Gradual Guarantee with Type Parameters
Rule gpc-as-forallLL is slightly different from rule gpc-as-forallL in the original algorithmic system in that we replace a with a new static existential variable α
bS . Note that αbS
might be solved to a gradual existential variable later. The rest of the rules are the same as
those in the original system.

5.4.2 Extended Instantiation
The instantiation judgments shown in Figure 5.4 also change significantly. The complication
comes from the fact that now we have two different kinds of existential variables, and therelative order that they appear in the context affects their solutions.
Rules gpc-instl-solveS and gpc-instl-solveG are the refinement to rule gpc-instlsolve in the original system. The next two rules deal with situations where one side is anexistential variable and the other side is an unknown type. Rule gpc-instl-solveUS is a
special case of rule gpc-as-unknownRR where we create a new gradual existential variableαbG and set the solution of αbS to be αbG in the output context. Rule gpc-instl-solveUG isthe same as rule gpc-instl-solveU in the original system and simply propagates the input
context. The next two rules gpc-instl-reachSG1 and gpc-instl-reachSG2 are a bit involved, but they both answer to the same question: how to solve a gradual existential variable
when it is declared after some static existential variable. More concretely, in rule gpc-instlreachSG1, we feel that we need to solve βbG to another existential variable. However, simply
setting βbG = αbS and leaving αbS untouched in the output context is wrong. The reason isthat βbG could be a gradual existential variable created by rule gpc-as-unknownLL/gpc-asunknownRR and solving βbG to a static existential variable would result in the same problem
as we have discussed. Instead, we create another new gradual existential variable αbG and setb
the solutions of both αbS and βG to it; similarly in rule gpc-instl-reachSG2. Rule gpc-

instl-reachOther deals with the other cases (e.g., αbS ⪅ βbS , αbG ⪅ βbG and so on). In

those cases, we employ the same strategy as in the original system.
As for the other instantiation judgment, most of the rules are symmetric and thus omitted. The only interesting rule is gpc-instr-forallLL, which is similar to what we did forrule gpc-as-forallLL.

5.4.3 Algorithmic Typing and Metatheory
Fortunately, the changes in the algorithmic bidirectional system are minimal: we replace
every existential variable with a static existential variable. Furthermore, we proved that theextended algorithmic system is sound and complete with respect to the extended declarative
system. The full extended algorithmic system can be found in Appendix B.

112

5.4 Extended Algorithmic Type SystemΓ `G αb⪅σa∆(Instantiation I)

gpc-instl-solveS

gpc-instl-solveGΓ, αbS , Γ′ `G αbS ⪅ τ a Γ, αbS = τ, Γ′Γ, αbG , Γ′ `G αbG ⪅ t a Γ, αbG = t, Γ′Γ `G τ

Γ `G tgpc-instl-solveUS

gpc-instl-solveUG

Γ[bα S ] `G αbS ⪅ ? a Γ[bαG , αbS = αbG ]Γ[bα G ] `G αbG ⪅ ? a Γ[bαG ]gpc-instl-reachSG1

Γ[bαS ][βbG ] `G αbS ⪅ βbG a Γ[bαG , αbS = αbG ][βbG = αbG ]gpc-instl-reachSG2

Γ[βbS ][bα G ] `G αbG ⪅ βbS a Γ[βbG , βbS = βbG ][bαG = βbG ]gpc-instl-forallR

gpc-instl-reachOtherb `G αΓ[bα][β]b ⪅ βb a Γ[bα][βb = αb]

Γ[bα], b `G αb ⪅ σ a ∆, b, Θ
Γ[bα ] `G αb ⪅ ∀b. σ a ∆gpc-instl-arrΓ[bα2 , αb1 , αb=αb1 → αb 2 ] `G σ 1 ⪅ α
b1 a Θ

Θ `G αb2 ⪅ [Θ]σ2 a ∆Γ[bα ] `G αb ⪅ σ1 → σ2 a ∆Γ `G σ ⪅ αba∆(Instantiation II, excerpt)
gpc-instr-forallLL

Γ[bα], ▶bbS , βbS `G σ[b 7→ βbS ] ⪅ αb a ∆, ▶bbS , ΘΓ[bα] `G ∀b. σ ⪅ αba∆Figure 5.4: Instantiation in the extended algorithmic system113

5 Restoring the Dynamic Gradual Guarantee with Type Parameters

5.4.4 Discussion
Do We Really Need Type Parameters in the Algorithmic System? As we mentioned
earlier, type parameters in the declarative system are merely an analysis tool, and in practice,type parameters are inaccessible to programmers. For the sake of proving soundness andcompleteness, we have to endow the algorithmic system with type parameters. However,
the algorithmic system already has static and gradual existential variables, which can serve
the same purpose. In that regard, we could directly solve every unsolved static and gradualexistential variable in the output context to Int and ?, respectively.

5.5 Restricted Generalization
In Section 4.3.2, we discussed the issue that the translation produces multiple target expressions due to the different choices for instantiations, and those translations have different
dynamic semantics. Besides that, there is another cause for multiple translations: redundant
generalization during translation by rule gen. Consider the simple expression (λx : Int. x) 1,
the following shows two possible translations:• ` (λx : Int. x) 1 : Int ⇝ (λx : Int. x) (hInt ,→ Inti1)• ` (λx : Int. x) 1 : Int ⇝ (λx : Int. x) (h∀a. Int ,→ Inti(Λa. 1))
The difference comes from the fact that in the second translation, we apply rule gen while
typing 1 to get • ` 1 : ∀a. Int. As a consequence, the translation of 1 is accompanied by acast from ∀a. Int to Int since the former is a consistent subtype of the latter. This difference isharmless, because obviously these two expressions will reduce to the same value in λB, thus
preserving coherence (up to cast error). While it is not going to break coherence, it does resultin multiple representative translations for one expression (e.g., the above two translations areboth the representative translations).
There are several ways to make the translation process more deterministic. For example,
we can restrict generalization to happen only in let expressions and require let expressions
to include annotations, as let x : σ = e1 in e2 . Another feasible option would be to give a
declarative, bidirectional system as the specification (instead of the type assignment one), in
the same spirit of DK [Dunfield and Krishnaswami 2013]. Then we can restrict generalization
to be performed through annotations in checking mode.
With restricted generalization, we hypothesize that now each expression has exactly one
representative translation (up to renaming of fresh type parameters). Instead of calling it a114

5.5 Restricted Generalization
representative translation, we can say it is a principal translation. Of course the above is onlya sketch; we have not defined the corresponding rules, nor studied metatheory.

115

Part IV
Type Inference with Promotion117

6 Higher-Rank Type Inference
Algorithm with Type Promotion
Designing type inference algorithms is challenging. In particular, while a declarative typesystem can guess a type (e.g., the type of x in \x. e) and can even be non-deterministic in theguessing (e.g., multiple possible types for x in \x. x), an type inference algorithm needs to be
deterministic, and thus has to deal with many low-level details including unification. At thesame time we expect an type inference algorithm to retain desirable properties like inferenceof principal types, as well as soundness and completeness with respect to the declarative typesystem. An algorithm is sound and complete, if it accepts and only accepts programs that are
well-typed in the declarative type system.
In this chapter, we focus on the design of type inference algorithms in the presence of
higher-rank polymorphism. Compared to type inference for simple types, type inference
for higher-rank polymorphism needs to further take care of scoping and dependency issues between different kinds of variables. We propose a strategy called promotion that helps
resolve the dependency of unification variables in the framework of type inference in context [Gundry et al. 2010]. To illustrate the key idea, Section 6.2 applies promotion to theunification algorithm for the simply typed lambda calculus. Section 6.3 then proposes polymorphic promotion to deal with subtyping for higher-rank polymorphism, which leads toan arguably simpler type inference algorithm for higher-rank polymorphism. Finally, we
briefly discuss how promotion can be further applied to other advanced features like dependent types and gradual types in Section 6.4. This chapter also sets up the stage for Chapter 7,
where promotion is used in a more complex setting.

6.1 Introduction and Motivation
6.1.1 Background:Type Inference in Context
Given a declarative type system, the goal of type inference is to reconstruct missing type
annotations for the system in a deterministic way that is also sound and complete with respect
to the declarative specification. In its standard form, a type inference algorithm represents

119

6 Higher-Rank Type Inference Algorithm with Type Promotion
missing type annotations using unification variables that will be solved during the process of
unification (or more generally, constraint solving).
Gundry et al. [2010] model unification and type inference for an ML-style polymorphic
system from a general perspective of information increase in the problem context. Specifically, the unification process is formalized as:Γ `u τ1 ≈ τ2 a ∆The judgment reads that under the input context Γ, unifying τ1 with τ2 yields the output context ∆. The type contexts (Γ and ∆) are used to to keep track of the solutions of unification
variables. We say that the output context ∆ is more informative than the original one Γ. For
example, the following derivation unifies αb with Int, which leads to the solution αb = Int.
αb, βb `u αb ≈ Int a αb = Int, βb
To ensure that the algorithm produces only solutions, the algorithm depends on the notionof well-founded contexts; that is, unification variable solutions may depend only on earlier
bindings. For example, αb, βb = αb → Int is well-founded, while αb = βb → Int, βb is not, as thebinding αb = βb → Int refers to the variable βb bound later in the type context. Well-founded
contexts effectively rule out invalid cyclic solutions like αb = βb → Int, βb = α
b → Int.

Besides contexts being ordered, a key insight of their approach lies in how to resolve thedependency between unification variables. Consider the following unification problem
αb, βb `u αb ≈ βb → Int
Here we cannot simply set α
b to βb → Int, as βb is out of the scope of αb. Thus, Gundry et al.
[2010] solve this problem by examining variables in the context from the tail to the head, andmoving segments of the context to the left if necessary. In this case, Gundry et al. [2010] wouldbα
return a solution context β,
b = βb → Int, where βb is moved to the front of αb. However,while moving type variables around is a feasible way to resolve the dependency between
unification variables, the unpredictable context movements make the information increase
hard to formalize and reason about. In their system, the information increase of contexts isdefined in a semantic way: a context Γ1 is more informative than another context Γ2 , if there
exists a substitution such that every item in Γ2 is, after context substitution, well-formed
under Γ1 . This semantic definition makes it hard to prove metatheory formally, especiallywhen advanced features are involved.

1206.1 Introduction and Motivation
The Dunfield-Krishnaswami type system (DK) [Dunfield and Krishnaswami 2013] alsouses ordered contexts as the input and output of the type inference algorithm for a higherrank polymorphic type system (Section 2.3). Unlike Gundry et al. [2010], DK does it ina more syntactic way. In their type system, instantiation rules decompose type constructs
so that unification between unification variables can only happen between single variables,
which can then be solved by setting the one that appears later to the one that appears earlier.
This way, the information increase of contexts is modeled as the intuitive and syntactic definition of context extension (Γ −→ ∆), which allows for inductive reasoning and proofs. As
an example, consider we want to unify (≈) αb with βb → Int under the context αb, βb using DK’s
approach. Recall that we use the hole notation for manipulating contexts by inserting andreplacing declarations in the middle. For example, Γ[Θ] means Γ has the form ΓL , Θ, ΓR ; if
we have Γ[bα] = (ΓL , αb, ΓR ), then Γ[bα = τ ] = (ΓL , αb = τ, ΓR ).
∆=αb1 , αb2 , αb=αb1 → αb2 , βb∆ `u βb ≈ α
b1 a ∆[βb = αb1 ]∆[βb = αb1 ] `u αb2 ≈ Int a ∆[b
α2 = Int][βb = αb1 ]αb, βb `u αb ≈ βb → Int a ∆[b
α2 = Int][βb = α
b1 ]Let us step through the derivation. As αb is unified with an arrow type, we know that αbmust be an arrow. Thus the variable α
b is solved by an arrow type α
b1 → αb2 consisting of
two fresh unification variables. The two variables α
b1 and α
b2 are then unified with βb andbInt, respectively. Unifying α
b1 with βb solves βb by α
b1 , as αb1 appears in the context before β;b The final solution context is ∆[bor otherwise we can solve α
b1 by β.
α2 = Int][βb = αb1 ] =
αb1 , αb2 = Int, αb=αb1 → αb2 , βb = α
b1 .

Challenges. However, while the approach of decomposing type constructs works perfectly for this example so far, it has two drawbacks. First, it produces duplication: in orderto deal with both cases, the unification rules are duplicated for when the unification variable appears on the left (i.e., Γ `u αb ≈ τ a ∆), and when it appears on the right (i.e.,Γ `u τ ≈ αb a ∆). For example, the following two rules are symmetric to each other:Γ[bα1 , αb2 , αb=αb1 → αb2 ] `u αb 1 ≈ τ1 a ∆ 1∆1 `u αb2 ≈ [∆1 ]τ1 a ∆2

Γ[bα] `u αb ≈ τ1 → τ2 a ∆ 2

121

6 Higher-Rank Type Inference Algorithm with Type Promotion

Γ[bα1 , αb2 , αb=αb1 → αb2 ] `u αb 1 ≈ τ1 a ∆ 1∆1 `u αb2 ≈ [∆1 ]τ1 a ∆2

Γ[bα] `u τ1 → τ2 ≈ αb a ∆2
Worse, this kind of “duplication” would scale up with the number of type constructs in thesystem.
Second, while decomposition works for function types, it may not work easily for more
complicated types, e.g., dependent types. For example, consider the following unification
problem, in which Πa : τ1 .τ2 is a dependent type where a may appear in τ2 .
b
αb, βb `u αb ≈ Πa : β.ab which is ill-typed.
Here because βb appears after αb, we cannot directly set αb = Πa : β.a,b like before, in which case we have α
However, if we try to decompose the type Πa : β.ab=Πa : αb1 .bα2 , it is obvious that α
b2 should be solved by a. Then, in order to make the solution
well typed, we need to put a in the front of α
b2 in the context. However, this means that awould remain in the context, and it would be available for any later unification variables thatshould not have access to a.

6.1.2 Our Approach: Type Promotion
We propose the promotion process, which helps resolve the dependency between unification
variables. Promotion combines the advantages of Gundry et al. [2010] and DK: it is a simple
and predictable process, so that information increase can still be modeled as the syntactic
context extension; moreover, it does not cause any duplication.
To understand how promotion works, let us consider again the unification problem
αb, βb `u αb ≈ βb → Int
The problem here is that βb is out of the scope of αb so we cannot directly set αb = βb → Int.
Therefore, we first promote the type βb → Int. At a high level, the promotion process looks
for free unification variables in the type, and solves those out-of-scope unification variables
with fresh ones added to the front of αb, such that unification variables in the promoted typeare all in the scope of αb. In this case, we will solve βb with a fresh variable α
b1 , producing thecontextαb1 , αb, βb = αb1 `u αb≈αb1 → βb1226.1 Introduction and Motivation
Notice that α
b1 is inserted right before αb. Now after substitution βb with α
b1 , the unification
example becomesαb1 , αb, βb = αb1 `u αb≈αb1 → Intand α
b1 → Int is a valid solution for αb. Therefore, we get a final solution context α
b1 , αb =αb1 → Int, βb = α
b1 . Namely,
αb, βb `u αb ≈ βb → Int a α
b1 , αb=αb1 → Int, βb = αb1Comparing the result with the solution context we get from DK (bα1 , αb2 = Int, α
b=α
b1 →
α
b2 , βb = αb1 ), it is obvious that these two solutions are equivalent up to substitution.

Interpretation of Promotion. The key insight of the promotion process is that the relative order between unification variables does not matter for solving a constraint. Thus its task is
to “move” unification variables to suitable positions indirectly, by solving those out-of-scope
unification variables with fresh in-scope ones.
The observation seems to go against the design principle that the contexts are ordered.
However, ordering is still important for variables whose order matters. For instance, forpolymorphic types, the order between unification variables αb and type variables a is important, so we cannot set α
b to a under the context (bα, a). Moreover, ordering still preventsbb
invalid cyclic contexts, e.g., α
b = β → Int, β = αb → Int.

Unification for the Simply Typed Lambda Calculus. As a first illustration of the promotion process, Section 6.2 recasts the unification process for the simply typed lambda calculus (STLC) using the promotion process. Note that using promotion for unification isoverkill for STLC, but this system illustrates the key idea of promotion and sets us the stage
for later advanced applications.

6.1.3 Polymorphic Promotion
The promotion process we described so far only works for unification. In this section, we discuss applying promotion to polymorphic subtyping in a higher-rank polymorphic calculus.
The challenge raised by higher-rank polymorphism is that, instead of unification for typeequality, higher-rank polymorphism takes care of polymorphic subtyping, which allows an123

6 Higher-Rank Type Inference Algorithm with Type Promotion
expression to have a subtype of the expected type. On the other hand, subtyping also needs
to take unification into account at the same time. For example, for the subtyping constraintΓ `sub α
b <: IntFor this case, promotion works like before: it tries to promote unification variables in Int,
where there is none so the promoted type remains Int. We can then solve αb by Int, which gives
us the output context αb = Int. Indeed, the only valid solution in this case is αb = Int. Recall
that unification variables can only be solved by monotypes, which is what makes unification
decidable and have principal solutions.
As the above example shows, for monotypes, promotion works just like before. However,with higher-rank polymorphism, we may have polymorphic types in the constraint. Considerαb `sub ∀a. a → a <: αbHow can we promote the polymorphic type ∀a. a → a into a monotype which can serve asa valid solution for αb? One possible answer is to set αb = Int → Int, or αb = Bool → Bool.
In fact, the most general solution for this subtyping problem is αb = βb → βb with freshb Namely, we remove the universal quantifier in ∀a. a → a and replace the variable aβ.
with a fresh unification variable βb added to the front of αb, resulting in the solution context
bαbβ,
b = βb → β.

What if the subtyping relation is the other way around? Namely,αb `sub α
b <: ∀a. a → a
Now how can we promote the type ∀a. a → a? It turns out that this subtyping is actuallyunsolvable, as there is no monotype that can be a subtype of ∀a. a → a. Therefore, in thiscase, promoting ∀a. a → a will directly add the type variable a to the tail of the context topromote a → a. Since a is added to the tail, it means that a is out of the scope of αb andpromoting a → a would fail, which is exactly what we want. In fact, the promotion would
succeed only if the universally quantified variable is not used in the body of the polymorphic
type. For example, ∀a. Int → Int can be promoted to Int → Int, which is a valid solution forαb in αb `sub α
b <: ∀a. Int → Int.
From these observations, we extend promotion to polymorphic promotion, which is able toresolve the polymorphic subtyping relation for unification variables. Depending on whether

1246.2 Unification for the Simply Typed Lambda Calculus
the unification variable appears on the right or left, polymorphic promotion has two modes,
which we call the contravariant mode and the covariant mode respectively.
The contravariant mode promotes types as ∀a. a → a in the case of αb `sub ∀a. a → a <:αb, where the universal quantifier is removed and the type variable a is replaced by a fresh
unification variable added to front of the unification variable being solved.
On the other hand, the covariant mode promotes types as ∀a. a → a in the case of αb `subαb <: ∀a. a → a. In this case, promoting ∀a. a → a will directly add the type variable a tothe tail of the context. Since the type variable is out of the scope of the unification variablebeing solved, and promotion will succeed only if the variable is not used in the body of thepolymorphic type.
While promoting polymorphic types behaves differently according to the mode, the mode
does not matter for monotypes, as in both αb <: Int and Int <: αb, αb = Int would be the only
solution. Since function types are contravariant in codomains and covariant in domains,promoting a function type under a certain mode proceeds to promote its codomain under
the other mode and promote its domain under the original mode. For example, αb = (βb →b → (Int → Int) is a solution forβ)αb `sub α
b <: (∀a. a → a) → (∀a. Int → Int)
where (∀a. a → a) → (∀a. Int → Int) is promoted under the covariant mode, which means∀a. a → a is promoted under the contravariant mode and ∀a. Int → Int is promoted under
the original covariant mode.
Polymorphic Promotion for Subtyping. We illustrate polymorphic promotion by showing that the original instantiation relationship in DK [Dunfield and Krishnaswami 2013] canbe replaced by our polymorphic promotion process. Furthermore, we show that subtyping,which was built upon instantiation but now uses polymorphic promotion, remains soundand complete.

6.2 Unification for the Simply Typed Lambda Calculus
This section first introduces the simply typed lambda calculus (STLC), and then presents
a unification algorithm that uses the novel promotion mechanism. As we have mentioned
before, using promotion for unification is overkill for STLC. So the purpose of this section
is to illustrate the key idea of promotion in the context of STLC, which sets us the stage forlater advanced applications.

125

6 Higher-Rank Type Inference Algorithm with Type Promotion

Expressions
Monotypes
Contexts

e
τ
Ψ

::=
::=
::=

x | n | λx : τ. e | λx. e | e1 e2
Int | τ1 → τ2
• | Ψ, x : τΨ`e:τ(Typing)t-var(x : τ ) ∈ Ψ

t-int

Ψ`x:τ

Ψ ` n : Intt-lamannt-lamΨ ` λx : τ. e : τ1 → τ2

Ψ ` λx. e : τ1 → τ2

Ψ, x : τ1 ` e : τ2t-app

Ψ ` e1 : τ1 → τ2

Ψ, x : τ1 ` e : τ2Ψ ` e2 : τ 1

Ψ ` e1 e2 : τ2

Figure 6.1: Syntax and declarative typing of the simply typed lambda calculus.

6.2.1 Declarative System
The declarative specification of STLC is given in Figure 6.1.
Expressions e include variables x, integers n, annotated lambda abstractions λx : τ. e,
un-annotated lambda abstractions λx. e, and applications e1 e2 . We have only monotypes τ ,which includes the integer type Int and function types τ1 → τ2 . Contexts Ψ keep track of
the types of the variables.
The typing rules are standard. For unannotated lambdas (rule t-lamann), the system
guesses a monotype τ .

6.2.2 Algorithmic System
The syntax of the algorithmic system is given in Figure 6.2. Following DK [Dunfield andKrishnaswami 2013], algorithmic monotypes include unification type variables αb. Algorithmic contexts also contain declarations of unification type variables, either unsolved (bα) orsolved (bα = τ ). Complete contexts Ω contain only solved variables. We use the judgment
Γ `wf τ to indicate that all unification variables in τ are well-scoped, and Γ ok to indicate
well-formed contexts, where each binding is well-formed in its prefix context.
Following Dunfield and Krishnaswami [2013], we use contexts as substitutions on types.
We write [Γ]τ to mean Γ applied as a substitution to type τ , whose definition is given below.

1266.2 Unification for the Simply Typed Lambda Calculus

Expressions
Monotypes
Algorithmic Contexts
Complete Contexts

x | n | λx : τ. e | λx. e | e1 e2
Int | τ1 → τ2 | αb• | Γ, αb | Γ, αb=τ• | Ω, α
b=τ

::=
::=
::=
::=

e
τ
Γ, ∆, ΘΩΓ `wf τ(Well-formedness)
wf-arrow

wf-intΓ `wf τ1wf

Γ `wf αbΓ ` τ1 → τ2

Γ ok

• ok

α
b∈Γ

wf

Γ ` Int

wfc-empty

wf-evarΓ `wf τ2

(Well-formedness of algorithmic contexts)
wfc-evarΓ ok

wfc-evarSolvedαb∈/ fv(Γ)Γ okΓ `wf τ

Γ, α
b ok

α
b∈
/ fv(Γ)Γ, αb = τ okFigure 6.2: Syntax of algorithmic STLC[Γ]Int

=

Int

[Γ[b
α]]b
α

=

α
b

[Γ[b
α = τ ]]b
α

=

[Γ[b
α = τ ]]τ[Γ]τ1 → τ2=

[Γ]τ1 → [Γ]τ2

Algorithmic typing. Figure 6.3 defines algorithmic typing. The judgment Γ ` e : τ a ∆reads that under the input context Γ, the expression e has type τ , with the output context ∆.
Most rules are standard. In rule at-lamann, instead of guessing a type as in its declarative
counterpart, the rule generates a fresh unification variable αb as the type of x, and anotherbfresh unification variable β as the type of the body, and continues type-checking the body.
b
After getting the type τ of the body, we unify βb with [Θ1 ]τ , and return the result type αb → β,
where the trailing context Θ is discarded.
Here are several notable things about rule at-lamann. First, we do not directly returnαb → τ as the result type, as otherwise we cannot discard the type context Θ after x, since
the type τ may refer to unification variables in Θ. Discarding Θ is mainly an optimization
step. In order to safely remove Θ, we unify βb ≈ [Θ1 ]τ , and return α
b → βb as the resulttype instead, which ensures that the result type is well-formed under the context ∆ (where
both αbandβb are bound). Second, we do not simply unify βb ≈ τ , but βb ≈ [Θ1 ]τ , where127

6 Higher-Rank Type Inference Algorithm with Type PromotionΓ`e:τ a∆(Algorithmic typing)at-varat-lamann(x : τ ) ∈ Γ

at-intΓ`x:τ aΓΓ ` n : Int a Γ

at-lamΓ, x : τ1 ` e : τ2 a ∆Γ ` λx : τ1 . e : τ1 → τ2 a ∆

b x:α
Γ, α
b, β,
b ` e : τ a Θ1

at-appΓ ` e 1 : τ1 a Θ 1

Θ1 `u βb ≈ [Θ1 ]τ a ∆, x : αb, ΘΓ ` λx. e : α
b → βb a ∆Θ2 , αb ⊩u [Θ2 ]τ1 ≈ [Θ2 ]τ2 → αba∆Θ 1 ` e 2 : τ2 a Θ 2

Γ ` e1 e2 : αba∆Γ `u τ1 ≈ τ2 a ∆(Unification)u-arrow

u-reflΓ `u τ1 ≈ τ3 a ΘΓ `u τ ≈ τ a ΓΘ `u [Θ]τ2 ≈ [Θ]τ4 a ∆Γ `u τ1 → τ2 ≈ τ3 → τ4 a ∆u-evarL

u-evarRΓ `u αb ≈ τ1 a ∆[b
α = τ2 ]Γ `u τ1 ≈ αb a ∆[bα = τ2 ]

Γ `pr
α]
α
b τ1 ⇝ τ2 a ∆[b

Γ `pr
α]
α
b τ1 ⇝ τ2 a ∆[bΓ `prαb τ1 ⇝ τ2 a ∆pr-intΓ `prαb Int ⇝ Int a Γ(Promotion)
pr-arrowΓ `prαb τ1 ⇝ τ3 a ΘΘ `prαb [Θ]τ2 ⇝ τ4 a ∆Γ `prαb τ1 → τ2 ⇝ τ3 → τ4 a ∆pr-evarL

pr-evarRb α] `pr βb ⇝ βb a Γ[β][bb α]
Γ[β][bαbb `pr βb ⇝ βb1 a Γ[βb1 , α
Γ[b
α][β]
b][βb = βb1 ]αbFigure 6.3: Algorithmic typing, promotion and unification of algorithmic STLC

128

6.2 Unification for the Simply Typed Lambda Calculus
the context Θ1 is applied to τ (we do not apply Θ1 to βb as given βb is a fresh unificationb This maintains an invariant: invariable not referred anywhere yet, we know [Θ1 ]βb = β).
the unification derivation Γ `u τ1 ≈ τ2 a ∆, the types τ1 and τ2 are fully applied under Γ.
This invariant simplifies the unification process: we know that every unification variable we
encounter during unification is unsolved and we do not need to discuss the case when it issolved where we need to replace it with its solution.
In rule at-app, we first infers the type of the function τ1 , and the type of the argument
τ2 . As we know that τ1 must be an arrow type, we create a fresh unification variable αb as theresult type of the application, and unify [Θ2 ]τ1 ≈ [Θ2 ]τ2 → αb. Again notice we apply thecontext to the types before we unify them.
Unification. The judgment Γ `u τ1 ≈ τ2 a ∆ reads that under the input context Γ,
unifying τ1 with τ2 results in the output context ∆. Rule u-refl is our base case, and rule uarrow unifies the components of the arrow types. When unifying αb ≈ τ1 (rule u-evarL),
we cannot simply set α
b to τ1 , as τ1 might include variables bound to the right of αb. Instead,
we need to promote (⊩pr ) τ1 . After promoting τ1 to τ2 , every unification variable in τ2 isbound to the left of αb, and thus we can directly set αb = τ2 . Rule u-evarR is symmetric tobb ≈ β, either rule u-evarL and rule u-evarR could
rule u-evarL. Note that when unifying α
be tried; an implementation can arbitrarily choose between them.
Promotion. The promotion relation Γ `prαb τ1 ⇝ τ2 a ∆ given at the bottom of Figure 6.3
reads that under the input context ∆, promoting type τ1 yields type τ2 , so that τ2 is wellformed in the prefix context of αb, while retaining [∆]τ1 = [∆]τ2 . As τ2 is well-formed in theprefix context of αb, recall that in the unification rule (e.g. rule u-evarL) it is well-scoped tosolve αb by τ2 .
At a high-level, ⊩pr looks for free unification variables in τ1 . Integers are always wellformed (rule pr-int). Promoting a function recursively promotes its components (rule prb in Γ are unaffected (rule pr-evarL), as they arearrow). Variables bound to the left of αalready well-formed. In rule pr-evarR, a unification variable βb bound to the right of αb in
Γ is replaced by a fresh variable introduced to α
b’s left. Promotion is a partial operation, as
it requires βb either to be to the right or to the left of αb, i.e., βb 6= αb. There is yet anotherpossibility: if βb = αb, then no rule applies. This is a desired property, as the βb = α
b caseexactly corresponds to the “occurs-check” in a more typical presentation of unification. By
preventing promoting α
b to the left of αb, we prevent the possibility of an infinite substitution
when applying an algorithmic context. Note that rule u-refl solves the unification case
α
b≈α
b.

129

6 Higher-Rank Type Inference Algorithm with Type Promotion
Γ −→ ∆(Context Extension)
ext-var

ext-id• −→ •
ext-solvedΓ −→ ∆[∆]τ = [∆]τ ′Γ −→ ∆′

Γ, αb −→ ∆, αb

ext-solve

ext-add

Γ, αb −→ ∆, αb=τΓ −→ ∆, αbΓ, x : τ −→ ∆, x : τΓ, αb = τ −→ ∆, αb=τext-evar[∆]τ = [∆]τ ′Γ −→ ∆

Γ −→ ∆

Γ −→ ∆′

ext-addSolveΓ −→ ∆

Γ −→ ∆, αb=τ

Figure 6.4: Context extension

Examples. Below we give the derivation of αb, βb `u αb ≈ βb → Int discussed in Section 6.1.1. Notice how the type βb → Int is promoted to α
b1 → Int.
pr-evarR

pr-intbb⊣αb1 , αb, βb = αb1αb, βb ⊢prαb β ⇝ αb1 , αb, βb = αb1
αb1 , αb, βb = αb1 ⊢prαb Int ⇝ Int ⊣ αbb1 → Int ⊣ αb1 , αb, βb = αb1αb, βb ⊢prαb β → Int ⇝ ααb, βb ⊢u αb ≈ βb → Int ⊣ αb1 , αb=αb1 → Int, βb = αb1pr-arrowu-eval-r

The algorithm (correctly) rejects to unify αb ≈ αb → Int, where promotions fails due to“occurs-check”.
αb, βb ⊩prb ⇝???
α
b α

...

α
b, βb ⊩prb → Int ⇝αb αpr-arrowu-eval-r

Γ `u αb≈αb → Int
The case for αb≈αb is fine:Γ `u αb≈αbaΓ

u-refl6.2.3 Soundness and Completeness
Following Dunfield and Krishnaswami [2013], we first define the context extension judgment
Γ −→ ∆, given in Figure 6.4, which reads that Γ is extended by ∆. Context extension

1306.3 Subtyping for Higher-Rank Polymorphism
captures the notion of information increase. Essentially, if Γ −→ ∆, then ∆ contains more
information than Γ; for example, solutions to existing unification variables (rule ext-solve),
more unification variables, either unsolved (rule ext-add) or solved (rule ext-addSolve).
We now prove that our type promotion strategy and the unification algorithm are sound.
First, we show that except for resolving the order problem, promotion will not change thetype. Namely, the input type and the output type are equivalent after substitution by theoutput context. Moreover, the promoted type is well-formed under the prefix context of αb.
Theorem 6.1 (Soundness of Promotion). If Γ `prαb τ1 ⇝ τ2 a ∆, then [∆]τ1 = [∆]τ2 .

Moreover, given ∆ = ∆1 , αb, ∆2 , we have ∆1 `wf τ2 ,With soundness of promotion, we can prove that the unification algorithm and the algorithmic typing are also sound:
Theorem 6.2 (Soundness of Unification). If Γ `u τ1 ≈ τ2 a ∆, then [∆]τ1 = [∆]τ2 .
Theorem 6.3 (Soundness of Typing). Given ∆ −→ Ω, if Γ ` e : τ a ∆, then [Ω]∆ ` e :[Ω]τ .
Now we move to completeness. Note that in the following completeness statement werequire αb∈/ fv(τ1 ), or otherwise promotion would fail.
Theorem 6.4 (Completeness of Promotion). Given Γ −→ Ω, and Γ `wf αb, and Γ `wf τ1 , and[Γ]b
α=αb, and [Γ]τ1 = τ1 , if αb∈
/ fv(τ1 ), there exist τ2 , ∆ and Ω′ such that Γ −→ Ω′ andΩ −→ Ω′ and Γ `prαb τ1 ⇝ τ2 a ∆.
Theorem 6.5 (Completeness of Unification). Given Γ −→ Ω, and Γ `wf τ1 , and Γ `wf τ2 ,and [Γ]τ1 = τ1 , and [Γ]τ2 = τ2 , if [Ω]τ1 = [Ω]τ2 , there exist ∆ and Ω′ such that Γ −→ Ω′and Ω −→ Ω′ and Γ `u τ1 ≈ τ2 a ∆.
Theorem 6.6 (Completeness of Typing). Given Γ −→ Ω, and Γ `wf τ , if [Ω]Γ ` e : τ , thenthere exits ∆, Ω′ , τ ′ such that ∆ −→ Ω′ , Ω −→ Ω′ , and Γ ` e : τ ′ a ∆, and [Ω]τ ′ = τ ,6.3 Subtyping for Higher-Rank Polymorphism
In this section, we adopt the type promotion strategy to a higher-rank polymorphic type system from DK [Dunfield and Krishnaswami 2013]. We show that promotion can be further
extended to polymorphic promotion to deal with subtyping, which can be used to replace
the instantiation relation in the original DK system while preserving soundness and completeness.

131

6 Higher-Rank Type Inference Algorithm with Type Promotion

Types
Monotypes
Contexts

σ
τ
Ψ

::=
::=
::=

Int | a | σ1 → σ2 | ∀a. σ
Int | a | τ1 → τ2
• | Ψ, x : σ | Ψ, a

Ψ `DK σ1 <: σ2(Subtyping)dk-s-tvar

dk-s-inta∈ΨΨ `DK a <: adk-s-arrowΨ `DK Int <: Int

dk-s-forallLΨ `DK τΨ `DK σ[a 7→ τ ] <: σ2Ψ `DK ∀a. σ1 <: σ2Ψ `DK σ3 <: σ1Ψ `DK σ2 <: σ4Ψ `DK σ1 → σ2 <: σ3 → σ4
dk-s-forallRΨ, a `DK σ1 <: σ2Ψ `DK σ1 <: ∀a. σ2Figure 6.5: Syntax and subtyping for higher-rank polymorphism

6.3.1 Declarative System
The definition of types in DK (Figure 2.6 in Section 2.3.2) is repeated in Figure 6.5. Comparing to STLC, we have polymorphic types ∀a. σ and type variables a. The context Ψ keeps
track of the types of term bindings x : σ, as well as type variables a.
Figure 6.6 also gives the subtyping rules for higher-rank polymorphism, which is used
in Odersky and Läufer [1996] (Section 2.2.2) and Dunfield and Krishnaswami [2013]. We
use the judgment Ψ `DK σ1 <: σ2 to denote the subtyping relation in DK. In rule dk-sarrow, functions are contravariant on arguments, and covariant on return types. Rule dks-forallR simply puts the type variable a into the context, and rule dk-s-forallL guesses
a monomorphic instantiation of the type variable.

6.3.2 Algorithmic System
The syntax of the algorithmic system is given in Figure 6.6. We distinguish between polymorphic types σ and monotypes τ . Following Dunfield and Krishnaswami [2013], types areextended with the unification variable α
b. Note that while the system includes polymorphic
types, the solutions to unification variables αb = τ are restricted to monotypes; that is the
key to ensure algorithmic typing is decidable and have principal types. ▶αb is a scope marker,
subscripted by the unification variable αb, and is used only in and explained with subtyping.
The promotion mode ⊗ is either covariant (+) or contravariant (-). We can use -⊗ to flip
the promotion mode. Specifically,132

6.3 Subtyping for Higher-Rank Polymorphism

Types
Monotypes
Algorithmic Contexts
Complete Contexts
Promotion ModesστΓ, ∆, Θ
Ω
⊗

::=
::=
::=
::=
::=

Int | a | σ1 → σ2 | ∀a. σ | αbInt | a | α
b | τ 1 → τ2 | αb• | Γ, a | Γ, αb | Γ, αb = τ | Γ, ▶αb• | Ω, a | Ω, αb=τ+|-Γ `wf τ(Well-formedness)

wf-intwf-arrowΓ `wf τ1wf

Γ `wf τ2Γ ` τ1 → τ 2wf

Γ `wf a

Γ` α
b

Γ ok

a∈Γ

(Well-formedness of algorithmic contexts)

wfc-empty• ok

wf-tvarαb∈ΓwfΓ ` Int

wf-evarwfc-evar

Γ ok

α
b∈
/ fv(Γ)wfc-evarSolved

Γ ok

Γ, α
b ok

α
b∈
/ fv(Γ)Γ, αb = τ ok

wfc-tvarΓ okΓ `wf τa ∈/ fv (Γ)Γ, a okFigure 6.6: Types, contexts, and well-formedness of the algorithmic system133

6 Higher-Rank Type Inference Algorithm with Type Promotion
-(+)

=

-

-(-)

=

+

Subtyping. Figure 6.6 also includes the subtyping judgment Γ `sub σ1 <: σ2 a ∆, whichreads that, under the input context Γ, type σ1 is a subtype of σ2 , with the output context ∆.
The rules except the last two are the same as the algorithmic subtyping rules in DK. Let us
first briefly go through the subtyping rules before we introduce our two new rules. Rules stvar, s-int, and s-evar are self-explanatory. In rule s-arrow, we first solve the contravariant constraint σ3 <: σ1 , which returns the output context Θ. We then solve the covariant
constraint [Θ]σ2 <: [Θ]σ4 . Note that we apply the context Θ before we check the subtyping
derivation. Just like before, this maintains a general invariant: in the subtyping derivationΓ `sub σ1 <: σ2 a ∆, the types σ1 and σ2 are fully applied under Γ. Rule s-forallR simply
puts a into the context and continue to check σ1 <: σ2 , which returns the output context
Θ, a, ∆. As a goes out of the scope in the conclusion, the rule then discards a as well as thetrailing context Θ after a. Rule s-forallL puts a new unification variable as the instantiation of the quantifier, and continues to check σ1 [a 7→ αb] <: σ2 . Just like before, as αb goesout of the scope in the conclusion, we want to discard αb as well as its trailing context in the
conclusion. However, during the derivation, we may actually add more unification variables
right in front of αb that should also be removed. Therefore, we insert a marker ▶αb that marks
the start of the scope that will get discarded in the conclusion. In the conclusion, we thendiscard ▶αb together with its trailing context.
Note that in the design of Dunfield and Krishnaswami [2013], rule s-forallR and rule sforallL are overlapped, when both types are quantifiers. In practice, since rule s-forallRis invertible, one can apply it eagerly. A possible fix is to restrict rule s-forallL to only apply
when the right hand side is not a quantifier [Peyton Jones et al. 2007].
Now we turn to rule s-instL and rule instR, which are specific to this system. Recall thatin DK, subtyping between αb and σ replies on the instantiation rules, which are duplicatedfor the case when αb is on the left and the case when αb is on the right. Here, instead of
instantiation, we directly use polymorphic promotion to promote the possibly polymorphic
type σ into a monotype τ . Specifically, rule s-instL uses polymorphic promotion under thecovariant mode (+) and rule s-instR uses polymorphic promotion under the contravariant
mode (-). If promotion succeeds, we can directly set α
b to τ .
Polymorphic promotion. The judgment Γ `⊗αb σ ⇝ τ a ∆ reads that under the input
context Γ, promoting σ under promotion mode ⊗ yields type τ , so that τ is well-formed in
the prefix context of αb.

134

6.3 Subtyping for Higher-Rank PolymorphismΓ `sub σ1 <: σ2 a ∆(Subtyping)s-tvars-int

s-evarΓ[a] `sub a <: a a Γ[a]Γ `sub Int <: Int a ΓΓ[bα] `sub αb <: αb a Γ[bα]s-arrowΓ `sub σ3 <: σ1 a Θ

s-forallRΘ `sub [Θ]σ2 <: [Θ]σ4 a ∆Γ, a `sub σ1 <: σ2 a ∆, a, ΘΓ `sub σ1 → σ2 <: σ3 → σ4 a ∆Γ `sub σ1 <: ∀a. σ2 a ∆s-forallLΓ, ▶αb , αb `sub σ1 [a 7→ αb] <: σ2 a ∆, ▶αb , ΘΓ `sub ∀a. σ1 <: σ2 a ∆s-instLs-instRΓ[bα] `sub αb <: σ a ∆[b
α = τ]

Γ[bα] `sub σ <: αb a ∆[bα = τ]α]Γ[bα] `+αb σ ⇝ τ a ∆[bα]Γ[bα] `-αb σ ⇝ τ a ∆[bΓ `⊗αb σ ⇝τ a∆(Polymorphic Promotion)p-pr-forallLp-pr-forallRbαb ⇝τ a∆Γ[β,b] `-αb σ[a 7→ β]

Γ, a `+αb σ ⇝ τ a ∆, a

Γ[bα] `-αb ∀a. σ ⇝ τ a ∆p-pr-arrowΓ `-⊗αb σ 1 ⇝ τ1 a ΘΓ `+αb ∀a. σ ⇝ τ a ∆Θ `⊗αb [Θ]σ2 ⇝ τ2 a ∆Γ `⊗αb σ 1 → σ 2 ⇝ τ1 → τ2 a ∆p-pr-intΓ `⊗αb Int ⇝ Int a Γp-pr-evarLp-pr-evarRb α] `⊗ βb ⇝ βb a Γ[β][bb α]
Γ[β][bαbb `⊗ βb ⇝ βb1 a Γ[βb1 , α
Γ[b
α][β]
b][βb = βb1 ]
αbp-pr-tvar

Γ[a][bα] `⊗α]αb a ⇝ a a Γ[a][bFigure 6.7: Unification, subtyping and polymorphic promotion of the algorithmic system)135

6 Higher-Rank Type Inference Algorithm with Type Promotion
The only difference between these two promotion modes is how to promote polymorphic
types. Under the contravariant mode (rule p-pr-forallL), a monotype would make the final
type more polymorphic. Therefore, we replace the universal binder a with a fresh unification
variable αb and put it before αb. Otherwise, in rule p-pr-forallR, we put a in the context andpromote σ. Notice that since a is added to the tail of the context, it is not in the scope of αband can actually never be used in σ or otherwise promotion would fails. This makes sense, as
for a subtyping relation Γ `sub αb <: ∀a. σ to hold, a must not be used in σ. That means ∀a. σ
can only be types like ∀a. Int or ∀a. Int → Int, in which case αb can be promoted to Int or
Int → Int respectively. In the conclusion of the rule, we discard a in the return context. Notethat we can simplify the rule by directly requiring a ∈/ fv (σ), as in rule p-pr-forallRR
given below. This way we would not need to add a into the context and the rule would remainsound.
p-pr-forallRRa ∈/ fv (σ)Γ `+αb σ ⇝ τ a ∆Γ `+αb ∀a. σ ⇝ τ a ∆Rule p-pr-arrow flips the mode for codomains, and uses the same mode for domains.
When the type to be promoted is a monotype, rule p-pr-mono uses the promotion judgment(⊩pr ) directly. Note that for a monotype the mode does not matter, so the rest of the rules
apply in both modes. Rules p-pr-int, p-pr-evarL, and p-pr-evarR are the same as before.
Importantly, we now have a new rule pr-tvar for type variables a. Note again that promotion is a partial operation, as it requires a to be the left of αb, since the order of variable
matters. This effectively solves the scoping issue between type variables and unification variables. For example, if we want to derive αb, a `sub αb <: a, we should fail – a is not in thescope of αb and we cannot set α
b = a! This is exactly what rule pr-tvar does – promoting afails as this is no way we can promote it to be well-typed in the prefix context of αb.

Examples. Below we give some examples.
The first example is the subtyping derivation of αb `sub ∀a. a → a <: αb. According torule s-instR, we start by promoting ∀a. a → a under the contravariant mode ( − ). We thencall rule p-pr-forallL, which adds a fresh unification variable βb in front of αb and returns
b Note that βb promotes to itself under both modes as it appears to
the promoted type βb → β.

136

6.3 Subtyping for Higher-Rank Polymorphism
the left of αb (rule p-pr-evarL). We now return back to the subtyping derivation, and set setbαb = βb → β.
p-pr-evarLp-pr-evarL

bα
bα
β,
b `+αb βb ⇝ βb a β,
b

bα
bα
β,
b `-αb βb ⇝ βb a β,
b

bα
bα
β,
b `-αb βb → βb ⇝ βb → βb a β,bp-pr-arrowp-pr-forallLbαbαb `-αb ∀a. a → a ⇝ βb → βb a β,s-instRbααb `sub ∀a. a → a <: αb a β,
b = βb → βb

The second example is the subtyping derivation of αb `sub α
b <: ∀a. a → a. According torule s-instL, we start by promoting ∀a. a → a under the covariant mode (+). We then callrule p-pr-forallR. In this case we directly add the type variable a into the type context,
and continue to promote a → a. After one step of rule p-pr-arrow, we got stuck – note
how rule p-pr-tvar fails to apply in this case as a appears to the right of αb. So promotion(correctly) rejects this example.
αb, a `+αb a ⇝???
αb, a `+αb a → a ⇝???

p-pr-arrowαb `+αb ∀a. a → a ⇝???

p-pr-forallRαb `sub α
b <: ∀a. a → as-instL6.3.3 Soundness and CompletenessThe statement of soundness of promotion remains the same as before.
Theorem 6.7 (Soundness of Promotion). If Γ `prb, ∆2 , thenαb τ1 ⇝ τ2 a ∆, and ∆ = ∆1 , α∆1 `wf τ2 , and [∆]τ1 = [∆]τ2 .

Based on soundness of promotion, we prove that after polymorphic promotion, the promoted type is also well-formed under the prefix context of αb. Moreover, polymorphic promotion builds a subtyping relation according to the promotion mode: under the contravariant mode (-), the original type is a subtype of the promoted type; under the covariant mode(+), the promoted type is a subtype of the original type.
Theorem 6.8 (Soundness of Polymorphic Promotion). If Γ `⊗αb σ ⇝ τ a ∆, and ∆ =∆1 , αb, ∆2 , then ∆1 `wf τ2 . Moreoever, given ∆ −→ Ω,• if ⊗ = -, then [Ω]Γ `DK [Ω]σ <: [Ω]τ ; and137

6 Higher-Rank Type Inference Algorithm with Type Promotion• if ⊗ = +, then [Ω]Γ `DK [Ω]τ <: [Ω]σ.
With soundness of polymorphic promotion, next we show that the new subtyping judgment using polymorphic promotion instead of instantiation remains sound.
Theorem 6.9 (Soundness of Subtyping). If Γ `sub σ1 <: σ2 a ∆, and ∆ −→ Ω, then[Ω]Γ `DK [Ω]σ1 <: [Ω]σ2 .
Now we turn to completeness. The completeness of promotion is the same as before.
Theorem 6.10 (Completeness of Promotion). Given Γ −→ Ω, and Γ `wf αb, and Γ `wf τ ,and [Γ]b
α=αb, and [Γ]τ = τ , if αb∈
/ fv(τ ), there exist τ2 , ∆ and Ω′ such that Γ −→ Ω′ andΩ −→ Ω′ and Γ `prαb τ ⇝ τ2 a ∆.
Completeness of polymorphic promotion has two parts. If the unification variable appears on the left, then we promote the type under the covariant mode (+), or otherwise thecontravariant mode (-). Moreover, it also requires αb∈/ fv(σ).
Theorem 6.11 (Completeness of Polymorphic Promotion). Given Γ −→ Ω, and Γ `wf αb,and Γ `wf σ, and [Γ]bα=αb, and [Γ]τ = σ, and αb∈/ fv(σ),• if [Ω]Γ `DK [Ω]bα <: [Ω]σ, then there exist τ , ∆ and Ω′ such that Γ `+αb σ ⇝ τ a ∆;and
• if [Ω]Γ `DK [Ω]σ <: [Ω]bα, then there exist τ , ∆ and Ω′ such that Γ `-αb σ ⇝ τ a ∆.
Finally, we prove that our subtyping is complete. With this, we have proved our claimthat the original instantiation relation in DK can be replaced by the polymorphic promotion process, as the subtyping algorithm using polymorphic promotion remains sound andcomplete.
Theorem 6.12 (Completeness of Subtyping). Given Γ −→ Ω, and Γ `wf σ1 , and Γ `wf σ2 ,if [Ω]Γ `DK [Ω]τ1 <: [Ω]τ2 , there exist ∆ and Ω′ such that ∆ −→ Ω′ and Ω −→ Ω′ andΓ `sub [Γ]σ1 <: [Γ]σ2 a ∆.

6.4 Discussion
This section discusses two extensions of promotion, and we can see that promotion scales
naturally to other type features. The first extension explores dependent types, while the second extension considers gradual types.

138

6.4 Discussion

6.4.1 Promoting Dependent Types
In Section 6.1.1 we mentioned the drawback of decomposing type constructs that it cannot
be easily applied to more advanced types like dependent types. In this section, we discuss
how we can apply promotion to dependent types.
Consider rule pr-pi given below that promotes a dependent type Π a : τ1 . τ2 .
pr-pi

pr
Γ `pr
α
b τ1 ⇝ τ3 a Θ Θ, a `αb [Θ]τ2 ⇝ τ4 a ∆, aΓ `prαb Π a : τ1 . τ2 → Int ⇝ Π a : τ3 . τ4 a ∆Here we first promote τ1 , returning τ3 . Then we add a into the context to promote τ2 . Finally,
we return Π a : τ3 . τ4 and discard a in the output context.

b a.
Unfortunately, this design does not work. In particular, consider promoting Π a : β.
pr-evarL

pr-evarL

bα
b
b b b
β,
b `pr
α
b β ⇝ β a β, αbαβ,b, a ⊩prαb a ⇝???

bαb a → Int ⇝β,b ⊩prαb Π a : β.

pr-pib a. However, after we add a into the
We expect that the promotion would return Π a : β.
context to promote a, rule pr-tvar does not apply, as a is out of the scope of αb!
The issue can be fixed by changing rule pr-tvar to rule pr-tvarr to not consider theorder of type variables.
pr-tvarrΓ `prαb a⇝aaΓThen, while promotion resolves the ordering of unification variables, since there is noconstraint for type variables, it is not guaranteed anymore that the promoted type is wellformed in the prefix context of αb. Therefore, we need to adjust the rule of subtyping to checkexplicitly that the result is well-formed, i.e.,s-instLLb , ∆2Γ[bα] `+αb σ ⇝ τ a ∆1 , α∆1 `wf τΓ[bα] `sub αb <: σ a ∆1 , α
b = τ, ∆2s-instRRΓ[bα] `-αb σ ⇝ τ a ∆1 , α
b , ∆2∆1 `wf τΓ[bα] `sub σ <: αb a ∆1 , α
b = τ, ∆2139

6 Higher-Rank Type Inference Algorithm with Type Promotion
Xie and Oliveira [2017] include a more detailed discussion and formalization of applying
promotion to a dependently typed lambda calculus.

6.4.2 Promoting Gradual Types
We have shown that polymorphic promotion works for DK. A natural extension is to also apply polymorphic promotion to GPC (Chapter 4). Then the key is to show how to promote theunknown type. Since comparing with the unknown type does not impose any constraints,
we can simply replace it with a fresh unification variable:
p-pr-unknown

b
b b]
Γ[b
α] `⊗
α
b ? ⇝ β a Γ[β, αb b b.
For example, we have αb `prαb Int → ? ⇝ Int → β a β, αFor the extended GPC which restores the dynamic guarantee (Chapter 5), we can replace
the unknown type with a fresh gradual unification variables instead.
p-pr-unknownG

b
b b]
Γ[b
α] `⊗
α
b ? ⇝ βG a Γ[βG , αWith these rules it would be possible to apply polymorphic promotion to GPC. Note this
discussion is a sketch and we have not fully worked out the full algorithm yet.

6.5 Promotion in Action
The promotion operation is actually present in the Glasgow Haskell Compiler (GHC), thestate-of-the-art compiler for the Haskell programming language, though its form is different
than what we have presented.
In particular, instead of keeping all the unification variables ordered in the context, which
can be quite inefficient, GHC only keeps them in order with respect to type variables in thecontext, by assigning a level number to each unification variable and each type variable, andincrementing the level number whenever we enter a new skolemisation scope, i.e., when a
new type variable is brought into scope. When unifying αb ≈ τ where αb is level N , to ensure
well-scopedness, GHC checks that all free variables in τ are from level N or lower, and updatelevels when needed: if there is a unification variable βb in τ2 , where βb has level M > N , thenit allocates βb1 at level N and set βb = βb1 , which essentially implements the idea of promotion.

1406.5 Promotion in Action
In the following discussion, we refer to the promotion process described in this chapter asorder promotion, and the process in GHC as level promotion.
As an example, consider during type inference, we need to prove the following subtyping
constraint:• `sub ∀a. Int → a → ∀b. b <: Int → ∀c. (∀d. d → d) → c
Below, we work through the example using order promotion on the left, and then workthrough the example again using level promotion on the right1 . The reader is encouraged toread the left part first before moving to the right.
With promotion, we start with• `sub ∀a. Int → a → ∀b. b <: Int →∀c. (∀d. d → d) → c

Now let us go through the example again
with levels. We keep track of the current
global level, starting from 0:(0)• `sub ∀a. Int → a → ∀b. b <: Int →∀c. (∀d. d → d) → c

First, we apply rule s-forallL and instan- Now as before, we first instantiating a withtiate a with αb:αb `sub Int → α
b → ∀b. b <: Int →∀c. (∀d. d → d) → c

fresh unification variables αb, but unlike
before, we record that αb is created at level
0. In this case, we only created new unification variables, so the global level number stays the same:(0)αb(0) `sub Int → α
b → ∀b. b <:Int → ∀c. (∀d. d → d) → c

We then apply rule s-arrow and split the We continue by spitting the arrow types.
constraint into two constraints. As the Again because the argument is simply
contravariant constraint on arguments is Int <: Int, we proceed by solving the resimply Int <: Int, we continue with the turn type:covariant constraint on the return type:(0)αb `sub α
b → ∀b. b <: ∀c. (∀d. d → d) → c1αb(0) `sub α
b → ∀b. b <:∀c. (∀d. d → d) → c

The process we are going to describe is not exactly how GHC works for this example, but it is sufficient toillustrate the key idea of levels.

141

6 Higher-Rank Type Inference Algorithm with Type Promotion
We apply rule s-forallR and skolemise c: We now skolemise c on the right, and alsosubαb, c `

remember that c is skolemised at level 0.

α
b → ∀b. b <: (∀d. d → d) → c Moreover, since we created a new typevariable, we increase the global level number by 1:(1)αb(0), c(0) `sub α
b → ∀b. b <:(∀d. d → d) → c

We apply rule s-arrow again, and we first We split the function types, and the firstsolve the constraint on the argument type: constraint we get is:αb, c `sub ∀d. d → d <: αbbwhere d is instantiated with β:αb, c, βb `sub βb → βb <: α
b

(1)

α
b(0), c(0) `sub ∀d. d → d <: αbWe again instantiate d with fresh unifib Since the global level
cation variables β.
number is 1, βb is at level 1:
(1)

b
b
α
b(0), c(0), β(1)`sub βb → βb <: αb Web with βb → β.
At this point, we want to solve αb with βb → Now we want to solve αb However, βb is not in the scope of αβ.
b. So check their level numbers. Because βb has
we promote βb by creating a fresh α
b2 to the a level greater than 0, we promote βb by
b2 at level 0, and setting
left of αb and setting βb = α
b2 . We can then creating a fresh αsolve αb=αb2 → αb2 .
αb, c, βb `sub βb → βb <: αbaαb2 , αb=αb2 →
αb2 , c, βb = αb2βb = αb2 . But unlike before, since the context is not ordered, we can freely put α
b2 at
the end of the context:(1)bαb(0), c(0), β(1)`sub βb → βb <: αbabαb(0) = αb2 → αb2 , c(0), β(1)=αb2 , αb2 (0)
Going back to the covariant constraint Going back to the covariant constraint∀d. d → d <: c on the left, which can ∀d. d → d <: c on the left, which can bebe simply solved by instantiating d with βb2 simply solved by instantiating d with βb2 (1)and solving βb2 = c:αb2 , αb=αb2 → αb2 , c, βb = α
b2 `sub ∀b. b <:and solving βb2 = c, as c has a smaller level:(1)bαb(0) = αb2 → αb2 , c(0), β(1)=b2 , αb2 (0) `sub ∀b. b <: c a αb(0) = αb2 →caαb2 , αb=αb2 → αb2 , c, βb = α
b2 , βb2 = c αbαb2 , c(0), β(1)=αb2 , αb2 (0), βb2 (1) = c

142

6.6 Levels and Generalization
A key observation is that just like order promotion, level promotion captures preciselythat the relative order between unification variables does not matter, and well-scopedness are
ensured using levels. The advantage with level promotion is that the whole type context does
not need to be ordered, which can be implemented more efficiently in practice. While notshown in this example, just like order promotion, level promotion can fail for type variables.
For example, order promotion fails to promote c in αb, c `sub αb <: c; similarly, level promotion fails to promote c in c(1), αb(0) `sub αb <: c, where c has a level greater than αb.

6.6 Levels and Generalization
Levels are first introduced in Rémy [1992] to implement generalization more efficiently. Besides GHC, they have also been implemented in OCaml; Kiselyov [2013] provides a detailed
description of levels in the OCaml type checker. In this section, we first describe how levels are used to implement efficient generalization, and then discuss how we can implement
generalization in our setting of ordered type contexts. Efficiency is not a major focus in thisdissertation, but we hope this discussion with levels and generalization can help provide adeeper understanding of ordered type contexts and promotion.
Let us first recall the declarative generalization rule used in the let expression in HindleyMilner:
hm-letΨ `HM e1 : σΨ, x : σ `HM e2 : τΨ `HM let x = e1 in e2 : τHow can we implement such a rule in an algorithmic system? In type inference literature
where efficiency is not the main concern, it is usually implemented as:Γ `HM e1 : τ1iαbi = fv (τ1 ) − fv (Γ)

iΓ, x : ∀ai i . τ1 [ αbi 7→ ai ] `HM e2 : τ2

Γ `HM let x = e1 in e2 : τ2
where Γ does not track unification variable, and we calculate the free variables in τ1 that are
not referred in Γ, and generalize type τ1 over ai i .
However, this implementation requires a linear scan of the type context, which can be verylarge for big programs. For nested let expressions, type checking will be quadratic. In fact, it
was one of the main reasons for the slow speed of Caml compilation [Rémy 1992].

143

6 Higher-Rank Type Inference Algorithm with Type Promotion
Rémy resolves the problem by using levels to speed up the process of generalization. The
key insight is to use levels to keep track of the scope of let expressions (in the previous section,
we showed how GHC uses levels to keep track of the scope of skolemisation). For example,-- starting from level 0
let x1 =-- level 1
let x2 = e2 -- level 2
in e3-- level 1
in e4-- level 0

When we enter a let expression, we increase the level, and when we get out of the scope of alet expression, we decrease the level. Every unification variable is associated with a level atwhich it is created. Now generalization can be implemented in an efficient way: instead oflooking through the context, we directly look at the type to be generalized, and compare thelevel of each unification variable with the current level. If a unification variable has a level
greater than the current level, it is created in a deeper level, but since that level is gone, it is
now a free variable and thus can be generalized over. Such an implementation can be further
optimized by updating levels lazily; we refer the interested reader to Kiselyov [2013]; Rémy
[1992] for more detailed explanations.
In our setting with ordered type contexts that tracks unification variables, we can also
implement generalization in a way more efficient than linearly scanning the type context:
as we ensure that all unification variables are put in order, we know exactly what unification
variables can be generalized over. With ordered type context, the let generalization rule looks
like:Γ, ▶αb `HM e1 : τ1 a ∆, ▶αb , Θiαbi = unsolved (Θ)

iibi 7→ ai ] `HM e2 : τ2 a ∆2 , x : ∀ai i . τ1 [ Xi 7→ ai ], Θ2
Γ, x : ∀ai i . ([Θ]τ1 )[ αΓ `HM let x = e1 in e2 : τ2 a ∆2
Note that to generalize τ1 , we do not need to traverse the whole context ∆, ▶αb , Θ. That is
because promotion has already guaranteed that any unification variables that Γ depends on
will already have been promoted to the left of the ▶αb , and so all unsolved unification variablesin Θ are exactly those unification variables that can be generalized over. We apply Θ to τ1
so that all solved unification variables are substituted in τ1 before we throw them away. We
can also be more precise by generalizing only unsolved (Θ) ∩ fv ([Θ]τ1 ). Notice that since
we keep discarding away contexts that go out of the scope (in this case Θ), we will not scana piece of a type context multiple times for nested lets.

144

7 Kind Inference for Datatypes
In recent years, languages like Haskell have seen a dramatic surge of new features that significantly extends the expressive power of their type systems. With these features, the challengeof kind inference for datatype declarations has presented itself and become a worthy research
problem on its own.
In this chapter, we apply promotion to kind inference for datatypes. Inspired by previous
research on type-inference, we offer declarative specifications for what datatype declarations
should be accepted, both for Haskell98 and for a more advanced system we call PolyKinds,
based on the extensions in modern Haskell, including a limited form of dependent types.
We believe these formulations to be novel and without precedent, even for Haskell98. These
specifications are complemented with implementable algorithmic versions. We study soundness, completeness and the existence of principal kinds in these systems, proving the properties where they hold. This work can serve as a guide both to language designers who wish
to formalize their datatype declarations and also to implementors keen to have principled
inference of principal types.

7.1 Introduction and Motivation
The global type-inference algorithms employed in modern functional languages such as Haskell,ML, and OCaml are derived from the Hindley-Milner type system (HM) [Damas and Milner
1982; Hindley 1969], with multiple extensions. Common extensions of HM include higherranked polymorphism [Odersky and Läufer 1996; Peyton Jones et al. 2007] and type-inference
for GADTs [Peyton Jones et al. 2006], which have both been formally studied thoroughly.
Most research work for extensions of HM so far (including OL, DK, AP and GPC) has
focused on forms of polymorphism, where type variables all have the same kind. In thesesystems, the type variables introduced by universal quantifiers and/or type declarations all
stand for proper types (i.e., they have kind ?). In such a simplified setting, datatype declarations such asdata Maybe a = Nothing | Just a145

7 Kind Inference for Datatypes
pose no problem at all for type inference: with only one possible kind for a, there is nothing
to infer.
However, real-world implementations for languages like Haskell support a non-trivial
kind language, including kinds other than ?. Haskell98 accepts higher-kinded polymorphism[Jones 1995], enabling datatype declarations such asdata AppInt f = Mk (f Int)
The type of constructor Mk applies the type variable f to an argument Int. Accordingly,
AppInt Bool would not work, as the type Bool Int (in the instantiated type of Mk) is invalid.
Instead, we must write something like AppInt Maybe: the argument to AppInt must be
suitable for applying to Int. In Haskell98, AppInt has kind (? → ?) → ?. For Haskell98style higher-kinded polymorphism, Jones [1995] presents one of the few extensions of HM
that deals with a non-trivial language of kinds. His work addresses the related problem ofinference for constructor type classes, although he does not show directly how to do inference
for datatype declarations.
Modern Haskell1 has a much richer type and kind language compared to Haskell98. In
recent years, Haskell has seen a dramatic surge of new features that extend the expressive
power of algebraic datatypes. Such features include GADTs, kind polymorphism [Yorgey et al.
2012] with implicit kind arguments, and dependent kinds [Weirich et al. 2013], among others.
With great power comes great responsibility: now we must be able to infer these kinds, too.
For instance, consider these datatype declarations:data App f a = MkApp (f a)data Fix f= In (f (Fix f ))data T = MkT1 (App Maybe Int)
| MkT2 (App Fix Maybe)-- accept or reject?

Should the declaration for T be accepted or rejected? In a Haskell98 setting, the kind of App
is (? → ?) → ? → ?. Therefore T should be rejected, because in MkT2 the datatype App
is applied to Fix :: (? → ?) → ? and Maybe :: ? → ?, which do not match the expected
kinds of App. However, with kind polymorphism, T is accepted, because App has the moregeneral kind ∀k. (k → ?) → k → ?. With this kind, both uses of App in T are valid.
The questions we ask in this section are these: Which datatype declarations should be accepted? What kinds do accepted datatypes have? Surprisingly, the literature is essentially silent
1

We consider the Glasgow Haskell Compiler’s implementation of Haskell, in version 8.8.

146

7.2 Overview
on these questions—we are unaware of any formal treatment of kind inference for datatypedeclarations.
Inspired by previous research on type inference, we offer declarative specifications for two
languages: Haskell98, as standardized [Peyton Jones 2003] (Section 7.3); and PolyKinds, asignificant fragment of modern Haskell (Section 7.6). These specifications are complemented
with algorithmic versions that can guide implementations (Sections 7.4 and 7.7). To relate thedeclarative and algorithmic formulations we study various properties, including soundness,completeness, and the existence of principal kinds (Sections 7.4.7, 7.5, and 7.7.6).

7.2 Overview
This section gives an overview of this work. We start by contrasting kind inference with
type inference, and then summarize the key aspects of the two systems of datatypes that wedevelop.

7.2.1 Kind Inference in Haskell98
Haskell98’s kind language contains a constant (the kind ?) and kinds built from arrows (k 1 →k 2 ). Kind inference for Haskell98 datatypes is thus closely related to type inference for thesimply typed λ-calculus (STLC). For example, consider a term + :: Int → Int → Int and atype constructor (: + :) :: ? → ? → ?. At the term level, we infer that add a b = a + b
yields add :: Int → Int → Int. Similarly, we can create a datatype
data Add a b = Add (a : + : b)
and infer Add :: ? → ? → ?.
No principal types. Consider now the function definition k a = 1. In the STLC, there
are infinitely many (incomparable) types that can be assigned to k, including k :: Int → Int
and k :: (Int → Int) → Int. Assuming that there are no type variables, the STLC accordingly
has no principal types. An analogous datatype declaration isdata K a = K Int
As with k, there are infinitely many (incomparable) kinds that can be assigned to K, including
K :: ? → ? and K :: (? → ?) → ?.

147

7 Kind Inference for Datatypes
Defaulting. Definitions like k (in STLC) or K (in Haskell98) do not have a principal
type/kind, which raises the immediate question of what type/kind to infer. Haskell98 solves
this problem by using a defaulting strategy: if the kind of a type variable cannot be inferred,then it is defaulted to ?. Therefore the kind of K in Haskell98 is ? → ?. From the perspective of type inference, such defaulting strategy may seem somewhat ad-hoc, but due to therole that ? plays at the type level it seems a defensible design for kind inference. Defaulting
brings complications in writing a declarative specification. We discuss this point further in
Section 7.4.3.

7.2.2 Kind Inference in Modern GHC Haskell
The type and kind languages for modern GHC are unified (i.e., types and kinds are indistinguishable), dependently typed, and the kind system includes the ? :: ? axiom Cardelli [1986];
Weirich et al. [2013]. We informally use the word type or kind where we find it appropriate.
Unlike Haskell98’s datatypes, whose inference problem is quite closely related to the wellstudied inference problem for STLC, type inference for various features in modern Haskell
is not well-studied. While we are motivated concretely by Haskell, many of the challenges weface would be present in any dependently typed language seeking principled type inference.
We use the term PolyKinds to refer to the fragment of modern Haskell that we model.2 We
enumerate the key features of this fragment below.
Kind polymorphism and dependent types Global type inference, in the style of Damas
and Milner [1982], allows polymorphic kinds to be assigned to datatype definitions. For
instance, reconsider
data K a = K Int
In PolyKinds, K can be given the kind K :: ∀{k }. k → ?. This example shows one of theinteresting new features of PolyKinds over Haskell98: kind polymorphism [Yorgey et al. 2012].
The polymorphic kind is obtained via generalization, which is a standard feature in DamasMilner algorithms. Polymorphic types are helpful for recovering principal types, since theygeneralize many otherwise incomparable monomorphic types.
System-F-based languages do not have dependent types. In contrast, PolyKinds supports
dependent kinds such asdata D :: ∀(k :: ?) (a :: k). K a → ?
2Some of the features we model are slightly different in our presentation than they exist in GHC. Appendix C.2
outlines the differences. These minor differences do not affect the applicability of our work to improving the
GHC implementations, but they may affect the ability to test our examples in GHC.

148

7.2 Overview
There are two noteworthy aspects about the kind of D. Firstly, kind and type variables are
typed: different type variables may have different kinds. Secondly, the kinds of later variables
can depend on earlier ones. In D, the kind of a depends on k. Both typed variables anddependent kinds bring technical complications that do not exist in many previous studies oftype inference (e.g., Peyton Jones et al. [2007]; Vytiniotis et al. [2011]).
First-order unification with dependent kinds and typed variables. Although PolyKinds is dependently typed, its unification problem is remarkably first-order. This is incontrast to many other dependently typed languages, where unification is usually higherorder [Andrews 1971; Huet 1973]. Since unification plays a central role in inference algorithms this is a crucial difference. Higher-order unification is well-known to be undecidable
in the general case [Goldfarb 1981]. As a consequence, type-inference algorithms for mostdependently typed languages make various trade-offs.
A key reason why unification can be kept as a first-order problem in PolyKinds is because
the type language does not include lambdas. Type-level lambdas have been avoided since
the start in Haskell, since they bring major challenges for (term-level) type inference [Jones
1995].
The unification problem for PolyKinds is still challenging, compared to unification for
System-F-like languages: unification must be kind-directed, as first observed at the term level
by Jones [1995]. Consider the following (contrived) example:
data X :: ∀a (b :: ? → ?). a b → ?

-- accepted

data Y :: ∀(c :: Maybe Bool). X c → ? -- rejected
In X’s kind, we discover a :: (? → ?) → ?. When checking Y ’s kind, we must infer how to
instantiate X: that is, we must choose a and b so that a b unifies with Maybe Bool, which is
c’s kind. It is tempting to solve this with a 7→ Maybe and b 7→ Bool, but doing so would be
ill-kinded, as a and Maybe have different kinds. Our unification thus features heterogeneous
constraints Gundry [2013]. When solving a unification variable, we need to first unify thekinds on both sides.
Because unification recurs into kinds, and because types are undifferentiated from kinds,
it might seem that unification might not terminate. In Section 7.7.4 we show that the firstorder unification with heterogeneous constraints employed in PolyKinds is guaranteed toterminate.
Mutual and polymorphic recursion Recursion and mutual recursion are omnipresent
in datatype declarations. In PolyKinds, mutually recursive definitions will be kinded together
and then get generalized together. For example, both P and Q get kind ∀(k :: ?). k → ?.

149

7 Kind Inference for Datatypes
data P a = MkP (Q a)data Q a = MkQ (P a)
The recursion is simple here: all recursive occurrences are at the same type. In existing
type-inference algorithms, such recursive definitions are well understood and do not bringconsiderable complexity to type inference. However, we must also consider polymorphic recursion as in Poly:
data Poly :: ∀k. k → ?
data Poly k = C1 (Poly Int) | C2 (Poly Maybe)This example includes a kind signature3 , meaning that we must check the kind of the datatype,not infer it. In the definition of Poly, the type Poly Int requires an instantiation k 7→ ?,while the type Poly Maybe requires an instantiation of k 7→ (? → ?). These differing
instantiations mean that the declaration employs polymorphic recursion.
PolyKinds deals with such cases of polymorphic recursion, which also appear at the termlevel—for example, when writing recursive functions over GADTs or nested datatypes [Birdand Meertens 1998]. Polymorphic recursion is known to render type-inference undecidable [Henglein 1993]. Furthermore, most existing formalizations of type inference avoid
the question entirely, either by not modeling recursion at all or not allowing polymorphic
recursion. Our PolyKinds system follows Jones [1999] to support polymorphic recursion
where there are kind signatures that cut the loops. Polymorphic recursion is allowed only on
datatypes with a kind signature; other datatypes are treated as monomorphic during inference.
Visible kind application PolyKinds lifts visible type application (VTA) [Eisenberg et al.
2016], whereby we can explicitly instantiate a function call, as in id @Bool True, to kinds,
giving us visible kind application (VKA). Following the design of VTA, we distinguish specified variables (a) from inferred variables ({a}). As described by Eisenberg et al. [2016, Section 3.1], only specified variables can be instantiated via VKA. Instantiation of variables isinferred when no explicit kind application is given. To illustrate, consider
data T :: ∀a b. a b → ?
Here, a and b are specified variables. Because their order is given, explicit instantiation ofa must happen before b. For example, T @Maybe instantiates a to Maybe. On the other3

Recently GHC has implemented standalone kind signatures [Eisenberg 2018], where instead of data it uses the
keyword type.

150

7.2 Overview
hand, the kind of a and b can be generalized to a :: k → ? and b :: k. Elaborating the kindof T , we write T :: ∀{k :: ?} (a :: k → ?) (b :: k). a b → ?. The variable k is inferred and is
not available for instantiation with VKA. This split between specified and inferred variables
supports predictable type inference: if the variables generated by the compiler (e.g., k) wereavailable for instantiation, then we have no way of knowing what order to instantiate them.
Open kind signatures and generalization order Echoing the design of Haskell, PolyKinds supports open kind signatures. We say a signature is closed if it contains no free variables, e.g.,
data T :: ∀a. a → ?
Otherwise, it is open, e.g.,
data Q :: ∀(a :: (f b)) (c :: k). f c → ?
Free variables (in this case, f , b, k) will be generalized over; that is, Haskell implicitly adds
universal quantifiers for each variable mentioned in the open kind signature that is not otherwise bound4 [Marlow et al. 2010]. We have a decision to make: in which order do we
generalize the free variables? This question is non-trivial, as there can be dependency between the variables. We infer k :: ?, f :: k → ?, b :: k. Even though f and b appear beforek, their kinds end up depending on k and we must quantify k before f and b. Inferring thisorder is a challenge: we cannot know the correct order before completing inference. We thus
introduce local scopes, which are sets of variables that may be reordered. Since the ordering
is not fixed by the programmer, these variables are considered inferred, not specified, withrespect to VKA.
Existential quantification. PolyKinds supports existentially quantified variables on
datatype constructors. This is useful, for example, to model GADTs. Given
data T1 = ∀a. MkT1 a
we get MkT1 :: ∀(a :: ?). a → T1. The type of the data constructor declaration can also be
generalized. Given
data P1 :: ∀(a :: ?). ?
from data T2 = MkT2 P1, we infer MkT2 ::∀{a::?}. P1 @a → T2, where P1 is elaborated
to P1 @a with a generalized as an inferred variable.
4

GHC actually implements the forall-or-nothing rule, which states that either all variables are quantified bya user-written ∀, or none are. Our treatment here is more general than forall-or-nothing. Appendix C.2.10
includes more discussion.

151

7 Kind Inference for Datatypes

7.2.3 Desirable Properties for Kind Inference
The goal of this work is to provide concrete, principled guidance to implementors of dependently typed languages, such as GHC/Haskell. It is thus important to be able to describe ourinference algorithm as sound and complete against a declarative specification. This declarative specification is what we might imagine a programmer to have in her head as she programs. This system should be designed with a minimum of low-level detail and a minimumof surprises. It is then up to an algorithm to live up to the expectations set by the specification.
The algorithm is sound when all programs it accepts are also accepted by the specification; it
is complete when all programs accepted by the specification are accepted by the algorithm.
Why choose the particular set of features described here? Because they lead to interestingkind inference challenges. We have found that the features above are sufficient in exploringkind inference in modern Haskell. We consider unformalized extensions in Section 7.8.

7.3 Datatypes in Haskell98
We begin our formal presentation with Haskell98. The fragment of the syntax of Haskell98
that concerns us appears at the top of Figure 7.1, including datatype declarations, types,
kinds, and contexts. The metavariable e refers to expressions, but we do not elaborate thedetails of expressions’ syntax or typing rules here. A program pgm is a sequence of groups(defined below) of datatype declarations T , followed by an expression e. We write τ1 → τ2
as an abbreviation for (→)τ1 τ2 .

7.3.1 Groups and Dependency Analysis
Users are free to write declarations in any order: earlier declarations can depend on laterones in the same compilation unit. However, any kind-checking algorithm must process
the declarations in dependency order. Complicating this is that type declarations may bemutually recursive. A formal analysis of this dependency analysis is not enlightening, so
we consider it to be a preprocessing step that produces the grammar in Figure 7.1. Thisdependency analysis breaks up the (unordered) raw input into mutually recursive groups(potentially containing just one declaration), and puts these in dependency order. We use
the term group to describe a set of mutually recursive declarations.

7.3.2 Declarative Typing Rules
The declarative typing rules are in Figure 7.1. There are no surprises here; we review theserules briefly. The top judgment is Σ; Ψ `pgm pgm : σ. Its rule pgm-dt extends the input

152

7.3 Datatypes in Haskell98
i

programdatatype decl.
data c’tor decl.

pgm
T
D

::=
::=
::=

rec Ti ; pgm | ejdata T ai i = Dj
D τi iexpression

e

::=

...

polytype
monotype
kind

σ
τ
κ

::=
::=
::=

∀ai : κi i . τInt | a | T | τ1 τ2 |→? | κ1 → κ2term context
type context

Ψ
Σ

::=
::=

• | Ψ, D : σ• | Σ, a : κ | Σ, T : κΣ; Ψ `pgm pgm : σ(Typing Program)
pgm-dtpgm-expr

Σ; Ψ ` e : σΣ′ = Σ, Ti : κii

Σ′ `dt Ti ; ΨiiiΣ′ ; Ψ, Ψi `pgm pgm : σiΣ; Ψ `pgm e : σΣ; Ψ `pgm rec Ti ; pgm : σΣ `dt T ; Ψ(Typing Datatype Decl.)
dt-decl(T : κi i → ?) ∈ ΣΣ, ai : κi i `dc
D j ; τj
Ta i

j

i

j

Σ `dt data T ai i = Dj ; Dj : ∀ai : κi i . τj′Σ `dc
τ D ;τ

j(Typing Data Constructor Decl.)
dc-declΣ `k τi i → τ : ?

i
i
Σ `dc
τ D τi ; τi → τΣ `k τ : κ(Kinding)

k-var

k-tcon(T : κ) ∈ Σk-natΣ `k a : κΣ `k T : κΣ `k Int : ?

(a : κ) ∈ Σk-app

Σ `k τ2 : κ1

k-arrowΣ `→: ? → ? → ?

Σ `k τ1 : κ1 → κ2Σ `k τ1 τ2 : κ2

Figure 7.1: Declarative specification of Haskell98 datatype declarations

153

7 Kind Inference for Datatypes
type context Σ with kinds for the datatype declarations to form Σ′ , which is used to check
both the datatype declarations and the rest of the program. In rule pgm-dt, we implicitlyiiextract the names T from the declarations T (and use this abuse of notation throughout
our work, relating T to T and D to D). The kinds are guessed for an entire group all at once:
they are added to the context before looking at the declarations. This is needed because thedeclarations in the group refer to one another. Guessing the right answer is typical of declarative type systems. The algorithmic system presented in Section 7.4 provides a mechanism
for an implementation. Although there is no special judgment for typing a group of mutuallyiirecursive datatypes, we use Σ `grp rec Ti ; κi i ; Ψi to denote that the kinding results ofidatatype declarations are κi i , and the output term contexts are Ψi .
Declarations are checked with Σ `dt T ; Ψ. This uses the guessed kinds to process thedata constructors of a declaration, producing a term context Ψ with the data constructors
and their types. The rule dt-decl ensures that the datatype has an appropriate kind in thecontext and then checks data constructors using the `dc judgment. These checks are donein a type context extended with bindings for the type variables ai i , where each ai has a kindextracted from the guessed kind of the datatype T . The subscript on the `dc judgment is thereturn type of the constructors, whose types are easily checked by rule dc-decl. The kinding
judgment Σ `k τ : κ is standard.

7.4 Kind Inference for Haskell98
We now present the algorithmic system for Haskell98. Of particular interest is the defaulting rule (Section 7.4.3), which means that these rules are not complete with respect to thedeclarative system.

7.4.1 Syntax
The top of Figure 7.2 describes the syntax of kinds and contexts in the algorithmic system
for Haskell98. The differences from the declarative system are highlighted in gray. Followb.
ing Dunfield and Krishnaswami [2013], kinds are extended with unification kind variables α
Algorithmic type contexts are also extended with unification kind variables, either unsolved(bα) or solved (bα = κ). Although the grammar for algorithmic term contexts Γ appears identical to that of declarative contexts (i.e., Ψ in Figure 7.1), note that the grammar for κ hasbeen extended; accordingly, algorithmic term contexts Γ might include kinds with unification variables, while declarative contexts Ψ do not. This approach of recording unification
variables and their solutions in the contexts is inspired by Gundry et al. [2010] and Dunfield
and Krishnaswami [2013]. Importantly, an algorithmic context is an ordered list, which en-1547.4 Kind Inference for Haskell98
forces that given ∆1 , α
b = κ, ∆2 , the kind κ must be well-formed under ∆1 . This rules out
b βb = αsolutions like αb = αb → ? or αb = β,
b. Complete contexts Ω are contexts with all
unification variables solved.
We use a hole notation for inserting or replacing declarations in the middle of a context.
∆[Θ] means that ∆ is of the form ∆1 , Θ, ∆2 . To reduce clutter, when we have ∆[bα], we also
use only ∆ to refer to the same context. If we have ∆[b
α ] = ∆1 , αb, ∆2 , then ∆[b
α = κ] =∆1 , αb = κ, ∆2 . This notation allows multiple holes: ∆[Θ1 ][Θ2 ] means that ∆ is of the formb is ∆1 , αb ∆3 . Critically, α∆1 , Θ1 , ∆2 , Θ2 , ∆3 . For example, ∆[bα][β]b, ∆2 , β,b appears beforebβ.

Since type contexts carry solutions for unification variables, we use contexts as substitutions: [∆]κ applies ∆ to kind κ. Applying ∆ substitutes all solved unification variables in its
argument idempotently. If under a complete context Ω, a kind κ is well-formed, then [Ω]κcontains no unification variables and is thus a well-formed declarative kind.
[∆]?

=

?

[∆]κ1 → κ2=[∆]κ1 → [∆]κ2

[∆[b
α]]b
α

=

α
b

[∆[b
α = κ]]bα=[∆[b
α = κ]]κ

For term contexts, [∆]Γ applies ∆ to each kind in Γ. Similarly, if under Ω, a term context Γis well-formed, then [Ω]Γ gives back a declarative term context.
[∆]•

=

•

[∆](Γ, D : σ)=[∆]Γ, D : [∆]σThe notation [Ω]∆ applies a complete context Ω to ∆. We apply Ω to the kind of type variables
and type constructors in ∆ and remove the binding of solved unification variables from ∆.
As above, [Ω]∆ is a declarative type context.
[•]•

=

•

[Ω, a : κ](∆, a : κ)=[Ω]∆, a : [Ω]κ[Ω, T : κ](∆, T : κ)=[Ω]∆, T : [Ω]κ[Ω, αb = κ](∆, α
b)

=

[Ω]∆

[Ω, α
b = κ](∆, αb = κ′ )=[Ω]∆if [Ω]κ = [Ω]κ′[Ω, αb = κ]∆

=

[Ω]∆

if α
b∈
/∆

7.4.2 Algorithmic Typing Rules
Figure 7.2 presents the typing rules for programs, datatype declarations and data constructor
declarations. As this work focuses on the problem of kind inference of datatypes, we reduce155

7 Kind Inference for Datatypes

kindterm context
type context
complete type context

κ
Γ
∆, Θ
Ω

::=
::=
::=
::=

? | κ1 → κ 2 | αb• | Γ, D : σ• | ∆, a : κ | ∆, T : κ | ∆, αb | ∆, αb=κ• | Ω, a : κ | Ω, T : κ | Ω, αb=κΩ; Γ ⊩pgm pgm : σ(Typing Program)a-pgm-expr[Ω]Ω; [Ω]Γ ` e : σΩ; Γ ⊩pgm e : σ

a-pgm-dti

Θi ⊩dt Ti ; Γi a Θi+1i

Θ1 = Ω, αb i , Ti : αbiiiΘn+1 −→→ Ω′Ω; Γ ⊩pgm rec Ti

i∈1..nΩ′ ; Γ, Γi ⊩pgm pgm : σ; pgm : σ∆ ⊩dt T ; Γ a Θ(Typing Datatype Decl.)

a-dt-decl(T : κ) ∈ ∆

i

i

∆, α
bi ⊩u [∆]κ ≈ (bαi → ?) a Θ1 , α
b i = κiΘj , ai : κi i ⊩dc
Dj ; τj a Θj+1 , ai : κi i
Ta i

i

j

i

dt

∆ ⊩ data T ai = Djij∈1..nj; Dj : ∀ai : κi i . τj a Θn+1′
∆ ⊩dc
τ D ;τ aΘ

(Typing Data Constructor Decl.)
a-dc-decl∆ ⊩k τi i → τ : ? a Θ

i
i
∆ ⊩dc
τ D τi ; τi → τ a Θ

Figure 7.2: Algorithmic program typing in Haskell98156

7.4 Kind Inference for Haskell98∆ −→→Ω(Defaulting)a-ctxde-empty

a-ctxde-tvar

a-ctxde-tcon∆, a : κ −→→ Ω, a : κ∆, T : κ −→→ Ω, T : κ

∆ −→
→Ω

• −→
→•

∆ −→
→Ω

a-ctxde-kuvarSolved

a-ctxde-solve∆, αb = κ −→→ Ω, α
b=κ

∆, α
b −→
→ Ω, α
b=?

∆ −→
→Ω

∆ −→
→Ω

Figure 7.3: Defaulting in Haskell98.

the expression typing to the declarative system (rule a-pgm-expr); note the contexts used
there are declarative. For type-checking a group of mutually recursive datatypes (rule apgm-dt), we first assign each type constructor a unification variable αb, and then type-check(⊩dt ) each datatype definition (Section 7.4.4), producing the context Θn+1 . Then we default(Section 7.4.3) all unsolved unification variables with ? using Θn+1 −→→ Ω, and continue
with the rest of the program. Defaulting here means that the unsolved unification variablesof one group do not propagate to the rest of the program; accordingly, the input context
of ⊩pgm is always a complete context. Echoing the notation for the declarative system, we
write Ω ⊩grp rec Tiii; κi i ; Γi a Θ to denote that the results of type-checking a groupiof datatype declarations are the kinds κi i , the output term contexts Γi , and the final output
type context Θ.

7.4.3 Defaulting
One of the key properties of datatypes in Haskell98 is the defaulting rule, ∆ −→→ Ω, given
in Figure 7.3. In a datatype definition, if a type parameter is not fully determined by thedefinitions in its mutually recursive group, it is defaulted to have kind ?.
To understand how this rule affects code in practice, consider the following definitions:data Q1 a = MkQ1
data Q2-- Q1 :: (? → ?)= MkQ2 (Q1 Maybe)

data P1 a = MkP1 P2
data P2-- rejected-- P1 :: (? → ?) → ?

= MkP2 (P1 Maybe)-- accepted

One might think that the result of checking Q1 and Q2 would be the same as checking P1
and P2. However, this is not true. Q1 and Q2 are not mutually recursive: they will not be157

7 Kind Inference for Datatypes
in the same group and are checked separately. In contrast, P1 and P2 are mutually recursive
and are checked together. This difference leads to the rejection of Q2: after kinding Q1, theparameter a is defaulted to ?, and then Q1 Maybe fails to kind check. Our algorithm is afaithful model of datatypes in Haskell98, and this rejection is exactly what the step Θn+1 −→→Ω (in rule a-pgm-dt) brings.
Other design alternatives. One alternative design is to default in rule a-pgm-expr
instead of rule a-pgm-dt, as shown in rule a-pgm-expr-alt. This means constraints in one
group propagate to other groups, but not to expressions. Then Q2 above is accepted.
a-pgm-expr-alt

∆ −→
→Ω

[Ω]Ω; [Ω]Γ ` e : σ∆; Γ ⊩pgm e : σ
A second alternative is that defaulting happens at the very end of type-checking a compilation
unit. In this scenario, we wait to commit to the kind of a datatype until checking expressions.
Now we can accept the following program, which would otherwise be rejected. However, thisstrategy does not play along well with modular design, as it takes an extra action at a module
boundary.
data Q1 a = MkQ1
mkQ1

= MkQ1 :: Q1 Maybe

In the rest of this section, we stay with the standard, doing defaulting as portrayed in
Figure 7.2.

7.4.4 Checking Datatype Declarations
The judgment ∆ ⊩dt T ; Γ a Θ checks the datatype declaration T under the input context∆, returning a term context Γ and an output context Θ. Its rule a-dt-decl first gets the kind
κ of the the type constructor from the context. It then assigns a fresh unification variable αbito each type parameter. The expected kind of the type constructor is αbi → ?. The rule theniunifies κ with α
bi → ?. Before unification, we apply the context ∆ to κ, thus [∆]κ; unification (Section 7.4.6) requires its inputs to be inert with respect to the context substitution.
Our implementation of unification guarantees that all the αbi will be solved, as reflected in therule a-dt-decl. The type parameters are added to the context to type check each data coni

structor. Checking the data constructor Dj returns its type τj and the context Θj+1 , ai : αbi .
Note that each output context must be of this form as no new entries are added to the end of158

7.4 Kind Inference for Haskell98∆ ⊩k τ : κ a Θ(Kinding)

a-k-arrowa-k-tcon∆ ⊩k →: ? → ? → ? a ∆∆ ⊩k T : κ a ∆a-k-app∆ ⊩k τ1 : κ1 a Θ1

a-k-vara-k-nat(T : κ) ∈ ∆(a : κ) ∈ ∆∆ ⊩k Int : ? a ∆Θ1 ⊩k τ2 : κ2 a Θ2

∆ ⊩k a : κ a ∆Θ2 ⊩kapp [Θ2 ]κ1 • [Θ2 ]κ2 : κ3 a Θ∆ ⊩k τ1 τ2 : κ3 a Θ∆ ⊩kapp κ1 • κ2 : κ a Θ(Application Kinding)a-kapp-kuvar

a-kapp-arrow∆[b
α1 , αb2 , αb=αb1 → αb2 ] ⊩u αb1 ≈ κ a Θ∆ ⊩u κ1 ≈ κ a Θ∆[bα] ⊩kapp αb•κ:αb2 a Θ∆ ⊩kapp κ1 → κ2 • κ : κ2 a Θ∆ ⊩u κ1 ≈ κ2 a Θ(Kind Unification)a-u-arrowa-u-refl∆ ⊩u κ1 ≈ κ3 a Θ1

∆ ⊩u κ ≈ κ a ∆Θ1 ⊩u [Θ1 ]κ2 ≈ [Θ1 ]κ4 a Θ∆ ⊩u κ1 → κ2 ≈ κ3 → κ4 a Θ

a-u-kvarL

a-u-kvarR∆[bα] ⊩u αb ≈ κ a Θ[bα = κ2 ]∆[bα] ⊩u κ ≈ αb a Θ[bα = κ2 ]

∆ `pr
α]
α
b κ ⇝ κ2 a Θ[b

∆ `pr
α]
α
b κ ⇝ κ2 a Θ[b∆ `prαb κ1 ⇝ κ2 a Θa-pr-star∆ `prαb ?⇝?a∆(Promotion)a-pr-arrow∆ `prαb κ1 ⇝ κ3 a ∆ 1∆1 `prαb [∆1 ]κ2 ⇝ κ4 a Θ∆ `prαb κ1 → κ2 ⇝ κ 3 → κ4 a Θa-pr-kuvarLa-pr-kuvarRb α] `pr βb ⇝ βb a ∆[β][bb α]∆[β][bαbb `pr βb ⇝ βb1 a ∆[βb1 , α
∆[b
α][β]
b][βb = βb1 ]αbFigure 7.4: Algorithmic kinding, unification and promotion in Haskell98.

159

7 Kind Inference for Datatypes
the context during checking individual data constructors. We can then generalize the type
τj over type parameters, returning Θn+1 as the result context.
′
The data constructor declaration judgment ∆ ⊩dc
τ D ; τ a Θ type-checks a data con-structor, by simply checking that the expected type τi i → τ is well-kinded.

7.4.5 Kinding
The algorithmic kinding ∆ ⊩k τ : κ a Θ is given in Figure 7.4. Most rules are selfexplanatory. For applications (rule a-k-app), we synthesize the type for an application τ1 τ2 ,where τ1 and τ2 have kinds κ1 and κ2 , respectively. The hard work is delegated to the application kinding judgment.
Application kinding ∆ ⊩kapp κ1 • κ2 : κ a Θ says that, under the context ∆, applying
an expression of kind κ1 to an argument of kind κ2 returns the result kind κ and an output
context Θ. We require the invariants that [∆]κ1 = κ1 and [∆]κ2 = κ2 . Therefore, if the kind
is a unification variable αb (rule a-kapp-kuvar), we know it must be an unsolved unification
variable. Since we know κ1 must be a function kind, we solve αb using α
b1 → αb2 , unify αb1with the argument kind κ, and return α
b2 . Note that the unification variables α
b1 and α
b2 areinserted in the middle of the context ∆; this allows us to remove the type variables from theend of the context in rule a-dt-decl and also plays a critical role in maintaining unification
variable scoping in the more complicated system we analyze later. If the kind of the function
is not a unification variable, it must surely be a function kind κ1 → κ2 (rule a-kapp-arrow),so we unify κ1 with the known argument kind κ, returning κ2 .

7.4.6 Unification
The unification judgment ∆ ⊩u κ1 ≈ κ2 a Θ is given in Figure 7.4. The elaborate styleof this judgment (with the promotion process ⊩pr ) is overkill for Haskell98, but this design
sets us up well to understand unification in the presence of our PolyKinds system, later. We
require the preconditions that [∆]κ1 = κ1 and [∆]κ2 = κ2 , so that every time we encountera unification variable, we know it is unsolved. Rule a-u-refl is our base case, and rule a-uarrow unifies the components of the arrow types. When unifying αb ≈ κ (rule a-u-kvarL),
we cannot simply set α
b to κ, as κ might include variables bound to the right of αb. Instead, weneed to promote (⊩pr ) κ. Rule a-u-kvarL first promotes the kind κ, yielding κ2 , so that κ2
is well-formed in the prefix context of αb. We can then set αb = κ2 in the concluding context.
Rule a-u-kvarR is symmetric to rule a-u-kvarL.

160

7.4 Kind Inference for Haskell98∆ −→ Θ

(Context Extension)

a-ctxe-empty

a-ctxe-tvar

a-ctxe-tcon∆, a : κ −→ Θ, a : κ∆, T : κ −→ Θ, T : κ∆ −→ Θ

• −→ •∆ −→ Θ

a-ctxe-solve

a-ctxe-kuvarSolved

a-ctxe-kuvar∆ −→ Θ

∆ −→ Θ

∆, αb −→ Θ, αb[Θ]κ1 = [Θ]κ2∆ −→ Θ∆, αb = κ1 −→ Θ, αb = κ2∆, αb −→ Θ, αb=κ

a-ctxe-add

a-ctxe-addSolved∆ −→ Θ, αb∆ −→ Θ, αb=κ∆ −→ Θ

∆ −→ Θ

Θ ⊩kv κΘ ⊩kv κFigure 7.5: Context extension in Haskell98

Promotion. As described in Section 6.1.2, the crucial insight of ⊩pr is that the relative
order between unification variables does not matter for solving a constraint. The promotion
judgment ∆ `prαb κ1 ⇝ κ2 a Θ says that, under the context ∆, we promote the kind κ1 ,yielding κ2 , so that κ2 is well-formed in the prefix context of αb, while retaining [Θ]κ1 =[Θ]κ2 . The promotion rules here are essentially the same as in Figure 6.3. Importantly, inrule a-pr-kuvarR, a unification variable βb bound to the right of αb in ∆ is replaced by a fresh
variable introduced to α
b’s left. It is this promotion algorithm that guarantees that all the αbiwill be solved in rule a-dt-decl: those variables will appear to the right of the unification
variable invented in rule a-pgm-dt and will be promoted (and thus solved).

7.4.7 Soundness and Completeness
The main theorem of soundness is for program typing:
Theorem 7.1 (Soundness of ⊩pgm ). If Ω ok, and Ω ⊩ectx Γ, and Ω; Γ ⊩pgm pgm : σ, then[Ω]Ω; [Ω]Γ `pgm pgm : σ.
This lemma statement refers to judgments Ω ok and Ω ⊩ectx Γ; these basic well-formedness
checks are standard. Because the declarative judgment `pgm requires declarative contexts,
we write [Ω]Ω and [Ω]Γ in the conclusion, applying the complete algorithmic context Ω as a
substitution to form a declarative context, free of unification variables.
The statement of completeness relies on the definition of context extension ∆ −→ Θ [Dunfield and Krishnaswami 2013]. The judgment captures a process of information increase. The
formal definition of context extension is given in Figure 7.5, and its treatment is as Dunfield161

7 Kind Inference for Datatypes
and Krishnaswami [2013]. Intuitively, context extension preserves all information in ∆, andmay increase the information by adding or solving more unification variables. In all thealgorithmic judgments, the output context is an extension of the input context.
We prove that our system is complete only up to checking a group of datatype declarations.

Theorem 7.2 (Completeness of ⊩grp ). Given Ω ok, if [Ω]Ω `grp rec Ti
i

i

i

i

i

i

; κi i ; Ψi , thenithere exists κ′i , Γi , Θ, and Ω′ , such that Ω ⊩grp rec Ti ; κ′i ; Γi a Θ, where Θ −→ Ω′ ,ii

and [Ω′ ]κ′i = κi , and Ψi = [Ω′ ]Γi .

The theorem statement uses the notational convenience for checking groups, defined in
Section 7.3.2 and Section 7.4.2. The theorem states that for every possible declarative typing
for a group, the algorithmic typing results can be extended to support the declarative typing.
Unfortunately, the typing program judgment `pgm is incomplete, as our algorithm models
defaulting, while the declarative system does not. (For example, the Q1/Q2 example of Section 7.4.3 is accepted by the declarative system but rejected by both GHC and our algorithmic
system.) We remedy this in the next section.

7.5 Type Parameters, Principal Kinds and Completeness in
Haskell98
We have seen that our judgments for checking programs `pgm and ⊩pgm do not support completeness, because the declarative system cannot easily model the defaulting rule given in
Section 7.4.3. In Chapter 5, we have seen that introducing type parameters [Garcia and Cimini 2015] helps resolve the dynamic gradual guarantee. Inspired by that, in this section,
we introduce kind parameters, and relate the defaulting rule to principal kinds to recovercompleteness.

7.5.1 Type Parameters
Consider the datatype data App f a = MkApp (f a) again. The parameter a in this example
can be of any kind, including ?, ? → ?, or others. To express this polymorphism without
introducing first-class polymorphism, we endow the declarative system with a set of kindparameters. Importantly, kind parameters live only in our reasoning; users are not allowed
to write any kind parameters in the source. We amend the definition of kinds in Figure 7.1
as follows.

162

7.5 Type Parameters, Principal Kinds and Completeness in Haskell98

kind parameter

P

kind

κ

∈
::=

KParam? | κ1 → κ2 | P

Kind parameters are uninterpreted kinds: there is no special treatment of kind parametersin the type system. Think of them as abstract, opaque kind constants. Kind parameters are
eliminated by substitutions S, which map kind parameters to kinds, and homomorphicallywork on kinds themselves. For example, App can be assigned kind (P → ?) → P → ?. By
substituting for P , we can get, for example, (? → ?) → ? → ?. Indeed, from (P → ?) →P → ? we can get all other possible kinds of App. This leads to the definition of principal
kinds for a group; and to the property that for every well-formed group, there exists a list ofprincipal kinds.
Definition 18 (Principal Kind in Haskell98 with Kind Parameters). Given a context Σ, aiigroup rec Ti , and a list of kinds κi i , we say that the κi i are principal kinds of Σ and rec Ti , dei

i

i

i

noted as Σ ` rec Ti ;p κi i , if Σ `grp rec Ti ; κi i ; Ψi , and whenever Σ `grp rec Ti ;
i

i

i

i

κ′i ; Ψ′i holds, there exists some substitution S, such that S(κi ) = κ′i and S(Ψi ) = Ψ′i .
ii

Theorem 7.3 (Principality of Haskell98 with Kind Parameters). If Σ `grp rec Ti ; κi i ; Ψi ,iiithen there exists some κ′i such that Σ ` rec Ti ;p κ′i .

7.5.2 Principal Kinds and Defaulting
Using the notion of kind parameters, we can now incorporate defaulting into the declarative
specification of Haskell98. To this end, we define the defaulting kind parameter substitution
S⋆:
Definition 19 (Defaulting Kind Parameter Substitution). Let S ⋆ ∈ KParam → κ denotethe substitution that substitutes all kind parameters to ?.
Using S ⋆ , we can rewrite rule pgm-dt. Noteworthy is the fact that kind parameters only livein the middle of the derivation (in the κi ), but never appear in the results S ⋆ (κi ).
pgm-dtPiΣ `grp rec Ti ; κi i ; Ψi

i

i

i

Σ ` rec Ti ;p κi ii

Σ, Ti : S ⋆ (κi ) ; Ψ, S ⋆ (Ψi ) `pgm pgm : σiΣ; Ψ `pgm rec Ti ; pgm : σ

7.5.3 Completeness
The two versions of defaulting (the one above and ∆ −→→ Ω of Section 7.4.2) are equivalent. This fact is embodied in the following theorem, stating that the algorithmic system iscomplete with respect to the declarative system with kind parameters.

163

7 Kind Inference for Datatypes
Theorem 7.4 (Completeness of ⊩pgm with Kind Parameters). Given algorithmic contexts Ω,Γ, and a program pgm, if [Ω]Ω; [Ω]Γ `pgm pgm : σ, then Ω; Γ ⊩pgm pgm : σ.

7.6 Declarative Syntax and Semantics of PolyKinds
Having set the stage for kind inference for datatypes in Haskell98, we now present the declarative PolyKinds system. Our syntax is given in Figure 7.11. Compared to Haskell98, programs pgm now include datatype signatures S 5 . Data constructor declarations D support
existential quantification. Types and kinds are collapsed into one level; σ and K are now synonymous metavariables and allow prenex polymorphism, where variables in a kind binder
φ can optionally have (monomorphic) kind annotations. Monotypes τ and κ allow visible
kind applications τ1 @τ2 . Elaborated types µ, η are the result of elaboration, which decorates source types to make them fully explicit. This is done so that checking equality ofelaborated types is straightforward. The syntax for elaborated types contains inferred polymorphism ∀{φc }.µ, where complete free kind binders φc have all variables annotated. Elaborated monotypes ρ and ω share the same syntax as monotypes. We informally use only ρor ω for elaborated monotypes.

7.6.1 Groups and Dependency Analysis
Decomposition of signatures and definitions allows a more fine-grained control of dependency analysis. If T has a signature, and S depends on T , then we can kind-check S without
inspecting the definition of T , because we know the kind of T . In other words, S only depends on the signature of T , not the definition of T . The complete dependency analysis rule,
inspired by Jones [1999, Section 11.6.3], is:Definition 20 (Dependency Analysis in PolyKinds). All definitions form a graph, where• notes are either (a) a signature, or (b) a definition; and• edges indicate the dependency relation:(i) If the signature/definition of T1 mentions T2 , then:a) if T2 has a signature, the signature/definition of T1 depends on the signatureof T2 ;b) otherwise, the signature/definition of T1 depends on the definition of T2 .
5

Our use of sig, just like rec, is for the purpose of formalism only, not as a required keyword in the source
program.

164

7.6 Declarative Syntax and Semantics of PolyKindsiprogramdatatype signature
datatype decl.
data constructor decl.

pgm
S
T
D

::=
::=
::=
::=

sig S; pgm | rec Ti ; pgm | edata T : σjdata T ai i = Dj
∀φ. D τi i

type, kindmonotype, monokind
elaborated type, kindσ, Kτ, κ, ρ, ω
µ, η

::=
::=
::=

∀φ. σ | τ
? | Int | a | T | τ1 τ2 | τ1 @τ2 |→∀{φc }.µ | ∀φc . µ | ρ

term context
type context
kind binder list
complete kind binder list

Ψ
Σ
φ
φc

::=
::=
::=
::=

• | Ψ, D : µ• | Σ, a : ρ | Σ, T : η• | φ, a | φ, a : κ• | φc , a : ρ

Figure 7.6: Syntax of PolyKinds(ii) A definition depends on its signature.
We then perform strongly connected component analysis on the group, where mutually recursive definitions form a group, producing the grammar in Figure 7.11.
To avoid a type that mentions itself in its own kind, we disallow self-dependency or mutual
dependency involving signatures. For example, a groupdata T1 :: T2 a → ?
data T2 :: T1 → ?
is rejected, lest T1 be assigned type ∀(a :: T1). T2 a → ?. In other words, signatures do notform groups: they are always processed individually. Moreover, the definition of a datatypewhich has a signature does not join others in a group, as according to Definition 20, there
will be no dependency from datatypes on it. This simplifies the kinding procedure, as we will
see in the coming section.

7.6.2 Checking Programs
The declarative typing rules appear in Figure 7.7, with auxiliary functions defined in Figure 7.8. The judgment Σ; Ψ `pgm pgm : σ checks the program. From now on we omit the typing rule for expressions in programs, which is essentially the same as in Haskell98. Rule pgmsig processes kind signatures by elaborating and generalizing the kind, then adding it to thecontext Σ. The helper judgment Σ `sig S ; T : η checks a kind signature data T : σ.

165

7 Kind Inference for Datatypes

Σ; Ψ `pgm pgm : σ(Typing Program)
pgm-sig

Σ `sig S ; T : ηΣ, T : η; Ψ `pgm pgm : µ

Σ; Ψ `pgm sig S; pgm : µ
pgm-dt-ttSΣ `dt T ; Ψ1

(T : η) ∈ Σ

Σ; Ψ, Ψ1 `pgm pgm : µ

Σ; Ψ `pgm rec T ; pgm : µ
pgm-dt-tt

Σ, φci `ela ωi : ?

i

i

i

Σ, ∪φci , Ti : ωi `dt Ti ; Ψi

i

i

i

′
Σ, ∪φci , Ti : ωi `genϕc Ψ i ; Ψ iiiiΣ, Ti : ∀{φci }.ωi ; Ψ, Ψ′i [ Ti 7→ Ti @φci ] `pgm pgm : σiΣ; Ψ `pgm rec Ti ; pgm : σΣ `sig S ; T : ηi(Typing Signature)

sig-tteσdiΣ, φc1 `k ∀φ. σ : ? ; ∀φc . η

φ = fkv(σ)|φ| = |φc |Σ `sig data T : σ ; T : ∀{φc1 }.∀{φc }.η
Σ `dt T ; Ψ(Typing Datatype Decl.)

dt-tt(T : ∀{φc1 }.∀φc2 . ωi i → ?) ∈ ΣdtΣ`Σ, φc1 , φc2 , ai : ωi i `dc
D ; µj(T @ϕc @ϕc a i ) j
1

j

Σ `dc
ρ D ; µ (Typing Data Constructor Decl.)
dc-tt

φc ∈
/ fkv(τi i )c kΣ, φ ` ∀φ. τi i → ρ : ? ; µ
i
c
Σ `dc
ρ ∀φ. D τi ; ∀{φ }.µΣ `genϕc Ψ 1 ; Ψ 2j(Generalization)gen

φc , φci ∈ Q(Σ, µi )
i

i

c c
Σ `genϕc Di : µi ; Di : ∀{φ , φi }.µi

Figure 7.7: Declarative specification of PolyKinds

166

2

data T ai = Dj ; Dj : ∀{φc1 }.∀φc2 . ∀ai : ωi i . µj
i

j

i

i

7.6 Declarative Syntax and Semantics of PolyKinds

eσd(Kind results in ?)sr-stare?dsr-arrow

sr-foralleκ1 → κ2 de∀φ. σdeκ2 dfkv(∀a. σ)
fkv(∀a : κ. σ)
fkv(τ1 τ2 )fkv(τ1 @τ2 )fkv(a)
fkv(?)
fkv(Int)
fkv(T )
fkv(→)

=
=
=
=
=
=
=
=
=

eσd

fkv(σ)\afkv(σ)\a ∪ fkv(κ)
fkv(τ1 ) ∪ fkv(τ2 )fkv(τ1 ) ∪ fkv(τ2 )
{a}
∅
∅
∅
∅

Q(Σ, σ) is the set of all well-formed orderings of Qκ (Σ, σ)Qκ (Σ, ∀a. σ)= Qκ (Σ, σ)\aQκ (Σ, ∀a : κ. σ) = Qκ (Σ, σ)\a ∪ Qκ (Σ, κ)Qκ (Σ, τ1 τ2 )= Qκ (Σ, τ1 ) ∪ Qκ (Σ, τ2 )Qκ (Σ, τ1 @τ2 )= Qκ (Σ, τ1 ) ∪ Qκ (Σ, τ2 )Qκ (Σ, a)= Qκ (Σ, ρ) ∪ {a : ρ} where a : ρ ∈ ΣQκ (Σ, ?)= ∅Qκ (Σ, Int)
= ∅Qκ (Σ, T )= ∅Qκ (Σ, →)= ∅Figure 7.8: Auxiliary functions of declarative specification in PolyKinds

167

7 Kind Inference for Datatypes
First, it uses eσd to ensure σ returns ?: eσd simply traverses over arrows and foralls, checking that the final kind of σ is ?. Then, as σ may be an open kind signature, it extracts thefree kind variables φ = fkv(σ), so that φ is a well-formed ordering of the free variables.
As discussed in Section 7.2.2, variables in φ are inferred so we accept any relative order, as
long as it features the necessary dependency between the variables. Then the rule tries toelaborate (`k ) the kind ∀φ. σ, where φ and φc have the same length (|φ| = |φc |). As theelaborated result ∀φc . η can be further generalized, we bring the free variables φc1 into scope
when elaborating. The concluding output is T : ∀{φc1 }.∀{φc }.η. As an example, consider
a kind signature ∀a. b → ?. We have φ = b, φc = b : ?, and φc1 = c : ?, and the final
kind is ∀{c : ?}.∀{b : ?}.∀(a : c). b → ?. We see in this one example the three sourcesof quantified variables, always in this order: variables arising from generalization (c), from
implicit quantification (b), and from explicit quantification (a).
Returning to the `pgm judgment, rule pgm-dt-ttS checks a datatype definition that has akind signature. It ensures that the signature has already been checked, by fetching the kind
information in the context using (T : η) ∈ Σ. Then it checks the datatype declaration,and gathers the output term context to check the rest of the program. Rule pgm-dt-tt, as in
Haskell98, guesses kinds ωi for each datatype Ti and puts Ti : ωi in the context before looking
at the declarations. The major difference from Haskell98 is that kinds can be generalized after
the group is checked. Thus we get rid of the issues of defaulting; that is, the example of Q1
and Q2 in Section 7.4.3 will be accepted in PolyKinds as instead of defaulting Q2 will get apolymorphic type. We use φci to denote the free variables in each kind ωi . After the recursive
group is typed, we generalize the kind of each type constructor as well as the type of its data
constructors. To generalize the type of data constructors, we use the `gen judgment. Rule gen
generalizes every data constructor in the context, where Q(Σ, σ) is the set of all well-formed
orderings of the free variables (transitively looking into variables’ kinds) of σ. φc are free typevariables of its corresponding type constructor, and φci are free type variables specific to thedata constructor. Returning to rule pgm-dt-tt, note that since the kinds of type constructors
are generalized, the occurrences of the type constructors now require more type arguments.
Therefore in Ψ′i , we substitute Ti with Ti @φci , where Ti is applied to all the variables boundin φci .
The judgment of checking datatype declarations Σ `dt T ; Ψ has only rule dt-tt, which
expands on the rule in Haskell98, to support top-level polymorphism for the kind of T .
Rule dc-tt supports existential variables φ. Moreover, the elaborated type µ of ∀φ. τi i →ρ can be further generalized over φc . Note that φc (via a small abuse of notation in the rule)
excludes free variables in τi and Σ.

168

7.6 Declarative Syntax and Semantics of PolyKindsΣ `inst µ1 : η <: ω ; µ2(Instantiation)
inst-forall

inst-reflΣ `ela ρ : ω1

Σ `inst µ : ω <: ω ; µΣ `inst µ1 @ρ : η[a 7→ ρ] <: ω2 ; µ2Σ `inst µ1 : ∀a : ω1 . η <: ω2 ; µ2

inst-forall-infer

Σ `ela ρ : ω1

Σ `inst µ1 @ρ : η[a 7→ ρ] <: ω2 ; µ2Σ `inst µ1 : ∀{a : ω1 }.η <: ω2 ; µ2
Σ `kc σ ⇐ ω ; µ(Kind Checking)kc-subΣ `k σ : η ; µ1

Σ `inst µ1 : η <: ω ; µ2

Σ `kc σ ⇐ ω ; µ2Σ `k σ : η ; µ(Kinding)

ktt-star

ktt-natΣ `k ? : ? ; ?

Σ `k Int : ? ; Int

ktt-appΣ `k τ1 : η1 ; ρ1

ktt-varktt-arrowΣ `k a : ω ; aΣ `k →: ? → ? → ? ;→(a : ω) ∈ ΣΣ `inst ρ1 : η1 <: (ω1 → ω2 ) ; ρ2

Σ `kc τ2 ⇐ ω1 ; ρ3

Σ `k τ1 τ2 : ω2 ; ρ2 ρ3
ktt-kapp

ktt-tconΣ `k κ1 : ∀a : ω. η ; ρ1

(T : η) ∈ ΣΣ `k T : η ; TΣ `kc κ2 ⇐ ω ; ρ2

Σ `k κ1 @κ2 : η[a 7→ ρ2 ] ; ρ1 @ρ2

ktt-kapp-inferΣ `k κ1 : ∀{ai : ωi i }.∀a : ω. η ; ρ′1Σ `ela ρi : ωi [ ai 7→ ρi i ]iΣ `kc κ2 ⇐ ω[ ai 7→ ρi i ] ; ρ′2Σ `k κ1 @κ2 : η[ ai 7→ ρi i ][a 7→ ρ2 ] ; ρ′1 @ρi i @ρ′2
ktt-foralli

Σ `ela ω : ?

Σ, a : ω `kc σ ⇐ ? ; µΣ `k ∀a. σ : ? ; ∀a : ω. µ
ktt-forallΣ `kc κ ⇐ ? ; ωΣ, a : ω `kc σ ⇐ ? ; µΣ `k ∀a : κ. σ : ? ; ∀a : ω. µ

Figure 7.9: Declarative kind-checking in PolyKinds

169

7 Kind Inference for Datatypes
Σ `ela µ : η(Elaborated Kinding)ela-starela-natelaelaΣ` ?:?

Σ `ela ρ1 : ∀a : ω. ηΣ` ω:?

Σ `ela ρ2 : ω1

Σ `ela ρ1 ρ2 : ω2

Σ `ela ρ2 : ωΣ `ela ρ1 @ρ2 : η[a 7→ ρ2 ]ela

Σ `ela T : ηΣ` a:ω
Σ `ela ρ1 : ω1 → ω2

ela-kapp(T : η) ∈ Σelaela-appΣ `ela →: ? → ? → ?

ela-forall(a : ω) ∈ ΣΣ ` Int : ?

ela-arrowela-tconela-varela

Σ, a : ω ` µ : ?

Σ `ela ∀a : ω. µ : ?

ela-kapp-infer

Σ `ela ρ1 : ∀{a : ω}.ηΣ `ela ρ2 : ωΣ `ela ρ1 @ρ2 : η[a 7→ ρ2 ]ela-forall-infer

Σ `ela ω : ?

Σ, a : ω `ela µ : ?

Σ `ela ∀{a : ω}.µ : ?

Figure 7.10: Declarative elaborated kinding in PolyKinds

7.6.3 Checking Kinds
The kinding judgment `k appears in Figure 7.9. Kinding Σ `k σ : η ; µ infers the type σto have kind η, and it elaborates σ to µ. The kinding rules are built upon the axiom Σ `k ? :? ; ? (rule ktt-star). While this axiom is known to violate logical consistency, as Haskell
is already logically inconsistent because of its general recursion, we do not consider it as an
issue here6 . Rule ktt-app concerns applications τ1 τ2 . It first infers the kind of τ1 to be η1 .
The kind η1 can be a polymorphic kind headed by a ∀, though it is expected to be a function
kind. Thus the rule uses `inst to instantiate η1 to ω1 → ω2 . The instantiation judgmentΣ `inst µ1 : η <: ω ; µ2 instantiates a kind η to a monokind ω, where if µ1 has kind ηthen µ2 has kind ω. After instantiation, rule ktt-app checks (`kc ) the argument τ2 against
the expected argument kind ω1 . The kind checking judgment `kc simply delegates the work
to kinding and instantiation. Rule ktt-kapp checks visible kind applications. Note in the
return kind η, the variable a is substituted by the elaborated argument ρ2 . Rule ktt-kappinfer is similar to rule ktt-kapp, but before we instantiated specified variables, we must first
instantiate all inferred variables. Rule ktt-foralli elaborates an unannotated type ∀a. σ to6This design is different from terminating dependently-typed languages, which require polymorphism to be
stratified into a hierarchy of levels, or otherwise they permit an encoding of Girard’s paradox [Girard 1972].
But as Haskell is already non-terminating, there is no motivation for stratification. See [Weirich et al. 2017]
for more discussion.

1707.7 Kind Inference for PolyKinds

elaborated monotype
term context
type context
complete type context
kind binder list

ρ, ω
Γ
∆, Θ

::=
::=
::=

Ω
φbc

|
::=
::=

? | Int | a | T | ρ1 ρ2 | ρ1 @ρ2 |→| αb• | Γ, D : µ• | ∆, a : ω | ∆, T : η∆, αb : ω | ∆, αb : ω = ρ | ∆, {∆′ } | ∆, ▶D• | Ω, a : ω | Ω, T : η | Ω, αb : ω = ρ | Ω, {Ω′ } | Ω, ▶D• | φbc , αb:κFigure 7.11: Algorithmic syntax in PolyKinds∀a : ω. µ, where ω is an elaborated kind (`ela ) guessed for a. Rule ktt-forall is similar,
elaborating an annotated type ∀a : κ. σ to ∀a : ω. µ, where ω is the elaborated result of κ.
The stand-alone elaborated kinding judgment `ela type-checks elaborated types, given in
Figure 7.10. As all necessary instantiation has been done, type-checking for elaborated types
is easy. For example, rule ela-app concerns applications ρ1 ρ2 . Compared to rule ktt-app,
here ρ1 has an arrow kind, and takes exactly the kind of ρ2 . All judgments output well-formed
elaborated types, as the following lemma states:
Lemma 7.5 (Type Elaboration). We have: 1. if Σ `k σ : η ; µ, then Σ `ela µ : η; 2. if
Σ `kc σ ⇐ η ; µ, then Σ `ela µ : η; 3. if Σ `ela µ1 : η, and Σ `inst µ1 : η <: ω ; µ2 , thenΣ `ela µ2 : ω.

7.7 Kind Inference for PolyKinds
We now describe the algorithmic counterpart of the PolyKinds system. Figure 7.11 presents
the syntax of kinds and contexts in the algorithmic system for PolyKinds. Elaborated monotypes are extended with unification variables αb. Echoing the algorithm for Haskell98, typecontexts are extended with unification variables, which now have kinds (bα : ω and αb:ω=ρ). Also added to contexts are local scopes {∆}. These are special type contexts, where
variables can be reordered. Recall the kind ∀(a :: (f b)) (c :: k). f c → ? in Section 7.2.2,
where f and b appear before k, but end up depending on k. In which order should we put
f , b and k in the algorithmic context to kind-check the signature? We cannot have a correct
order before completing inference. Therefore, we put them into a local scope, knowing we
can reorder the variables during kind-checking according to the dependency information.
The well-formedness judgment for local scopes requires them to be well-scoped, leading to
the fact that ∆, {∆′ } is well-formed iff ∆, ∆′ is. The marker ▶D , subscripted by the name ofa data constructor, is used only in and explained with rule a-dc-tt.

171

7 Kind Inference for Datatypes

7.7.1 Algorithmic Program Typing
The algorithmic typing rules appear in Figure 7.12, with some of the auxiliary definitions
given in Figure 7.13. The judgment Ω; Γ ⊩pgm pgm : µ checks the program. The rule apgm-sig and rule a-pgm-dt-ttS correspond directly to the declarative rules. Note that as
the datatype declaration in rule a-pgm-dt-ttS already has a signature, the output type context remains unchanged. Rule a-pgm-dt-tt concerns a group (without kind signatures).
Like in Haskell98, it first assigns a fresh unification variable α
bi : ? as the kind of each type
constructor, and then type-checks each datatype declaration, yielding the output context
Θn+1 . Unlike Haskell98 which then uses defaulting, here from each α
bi we get their unsolvedcunification variables φb and generalize the kind of each type constructor as well as the typei

of each data constructor. The unsolved (∆) metafunction (Figure 7.13) simply extracts a setof free unification variables in ∆, with their kinds substituted by ∆. Before generalization,
we apply Θn+1 to the results so all solved unification variables get substituted away. We use
the notation φbc 7→ φc to mean that all unification variables in φbc are replaced by fresh typei

i

i

variables in φci . The algorithmic generalization judgment ⊩gen corresponds straightforwardly
to the declarative rule, and thus is omitted. Though they appear daunting, the extended contexts used in the last premise to this rule are unsurprising: they just apply the relevant substitutions (the solved unification variables in Θn+1 , the replacement of unification variables
with fresh proper type variables φbc 7→ φc , and the generalization of the kinds of the groupii

of datatypes Ti 7→ Ti @φci ).
The judgment Ω ⊩sig S ; T : η type-checks a signature definition (rule a-sig-tt).
We get all free variables in σ using fkv(σ) and assign each variable ai a kind αbi : ?. Those
variables are put into a local scope to kind-check σ. Then, we use scoped_sort—a standard
topological sort—to return an ordering of the variables that respects dependencies. Finally,
we substitute away solved unification variables in the result kind µ and generalize over theunsolved variables φbc in ∆. As φbc is generalized outside φc , we use the quantification check221i∆ ,→ ai (Section 7.7.2) to ensure the result kind is well-ordered.

Rule a-dt-tt is a straightforward generalization of rule a-dt-decl to polymorphic kinds.
Here T can have a polymorphic kind from kind signatures.
Rule a-dc-tt checks a data constructor declaration. It first puts a marker into the context
before kinding. After kinding, it substitutes away all the solved unification variables to theright of the marker, and generalizes over all unsolved unification variables to the right of themarker. The fact that the context is ordered gives us precise control over variables that need
generalization.

172

7.7 Kind Inference for PolyKindsΩ; Γ ⊩pgm pgm : µ(Typing Program)a-pgm-sigΩ ⊩sig S ; T : ηΩ, T : η; Γ ⊩pgm pgm : µΩ; Γ ⊩pgm sig S; pgm : µ
a-pgm-dt-ttSΩ ⊩dt T ; Γ1 a Ω(T : η) ∈ ΩΩ; Γ, Γ1 ⊩pgm pgm : µΩ; Γ ⊩pgm rec T ; pgm : µ
a-pgm-dt-tti

Θ1 = Ω, αb i : ? , Ti : αbiiΘi ⊩dt Ti ; Γi a Θi+1

i

i
i
gen
c
c
′
b
Θn+1 ⊩ϕc ([Θn+1 ](Γi [ φi 7→ φi ])) ; Γi
i
i
i
i
i
Ω, Ti : ∀{φci }.(([Θn+1 ]bαi )[ φbci 7→ φci ]) ; Γ, Γ′i [ Ti 7→ Ti @φci ] ⊩pgm pgm : µi∈1..nΩ; Γ ⊩pgm rec Ti; pgm : µφbci = unsolved([Θn+1 ]bαi )iΩ ⊩sig S ; T : η(Typing Signature)

a-sig-ttiΩ, {bαi : ?, ai : αbi } ⊩k σ : ? ; η a ∆i
φc = scoped_sort(ai : [∆]bαi )
φbc = unsolved(∆)∆ ,→ ai i
eσd

ai i = fkv(σ)

1

sig

Ω⊩

2
c
data T : σ ; T : ∀{φ2 }.((∀{φc1 }.[∆]η)[φbc2 7→ φc2 ])∆ ⊩dt T ; Γ a Θ(Typing Datatype Decl.)

a-dt-tt(T : ∀{φc1 }.∀φc2 . ω) ∈ ∆ii∆, φc1 , φc2 , αbi : ? ⊩u [∆]ω ≈ (bαi → ?) a Θ1 , φc1 , φc2 , αb i : ? = ωiiΘj , φc1 , φc2 , ai : ωi i ⊩dc
D ; µj a Θj+1 , φc1 , φc2 , ai : ωi i
(T @ϕc @ϕc a i ) j
1dt∆ ⊩ data T ai = Dji∆ ⊩dc
ρ D ;µaΘj∈1..n2jij; Dj : ∀{φc1 }.∀φc2 . ∀ai : ωi i . µj a Θn+1

(Typing Data Constructor Decl.)

a-dc-tt∆, ▶D ⊩k ∀φ. (τi i → ρ) : ? ; µ a Θ1 , ▶D , Θ2
φbc = unsolved(Θ2 )∆ ⊩dc ∀φ. D τi i ; ∀{φc }.(([Θ2 ]µ)[φbc 7→ φc ]) a Θ1
ρFigure 7.12: Algorithmic program typing in PolyKinds

173

7 Kind Inference for Datatypes

unsolved (•)
unsolved (∆, a : ω)unsolved (∆, T : η)unsolved (∆, {∆lo })unsolved (∆, αb : ω1 = ω2 )unsolved (∆, αb : ω)

=
=
=
=
=
=

•
unsolved (∆)
unsolved (∆)
unsolved (∆, ∆lo )
unsolved (∆)
unsolved(∆), αb : [∆]ω(Quantification Checking)∆ ,→ φa-qc-loa-qc-tvar

a-qc-tcon∆ ,→ φ∆ ,→ φ∆, ∆lo ,→ φ∆, T : η ,→ φlo∆, a : ω ,→ φ∆, {∆ } ,→ φa-qc-kuvarSolved∆ ,→ φ∆, αb : ω1 = ω2 ,→ φa-qc-kuvar∆ ,→ φfkv([∆]ω) ] φ∆, αb : ω ,→ φFigure 7.13: Unsolved set of unification variables and quantification check in PolyKinds

7.7.2 The Quantification CheckIn rule a-sig-tt, we reject ill-ordered kinds. Consider the following example:
data Proxy :: ∀k. k → ?
data Relate :: ∀a (b :: a). a → Proxy b → ?
data T :: ∀(a :: ?) (b :: a) (c :: a) d. Relate b d → ?
Proxy just gives us a way to write a type whose kind is not ?. The Relate τ1 τ2 type forces thekind of τ2 to depend on that of τ1 , giving rise to the unusual dependency in T . The definition of T then introduces a, b, c and d. The kinds of a, b and c are known, but the kind of db where βb :: a. There are no further
must be inferred; call it αb. We discover that αb = Proxy β,b Naïvely, we would generalize over β,b but that would be disastrous, as aconstraints on β.

is locally bound. Instead, we must reject this definition, as our declarative specification always puts inferred variables (such as the type variable βb would become if generalized) before
other ones. Recall in Section 7.6.2, we always put quantified variables in this order: variables
arising from generalization, from implicit quantification, and from explicit quantification.
This order is intuitive: variables arising from generalization is invisible to the users and thus
are always at the front; variables from implicit quantification refer to user-written variables
and follow next; and variables from explicit quantification correspond to a user-specified
telescope and thus should not be modified.

174

7.7 Kind Inference for PolyKinds
The quantification-checking metafunction ∆ ,→ φ, given in Figure 7.13, essentially expresses fkv(unsolved (∆)) ] φ, which ensures that free variables in unsolved (∆) are disjoint(]) with φ, so that we can safely generalize unsolved (∆) outside φ.
Unfortunately, quantification check also makes us lose principality, as we will discuss in
Section 7.7.6. We conjecture, though, that lifting quantification check and allowing for interleaving specified/inferred variables may restore principality. That means, for the above
example, we can getdata T :: ∀(a :: ?) (b :: a) (c :: a) {f :: a} (d :: Proxy f ). Relate @a @f b d → ?
We also consider alternative design in Appendix C.2.8.

7.7.3 Kinding
Figure 7.14 presents the selected rules for kinding judgment ⊩k , along with the auxiliary
judgments. Full rules can be found in Appendix C.3. Most rules correspond directly to theirdeclarative counterparts. For applications τ1 τ2 , rule a-ktt-app first synthesizes the kind of
τ1 to be η1 , then uses ⊩kapp to type-check τ2 . The judgment ∆ ⊩kapp (ρ1 : η)•τ : ω ; ρ2 a Θ
is interpreted as, under context ∆, applying the type ρ1 of kind η to the type τ returns kindω, the elaboration result ρ2 , and an output context Θ. When η1 is polymorphic (rule a-kapptt-forall), we instantiate it with a fresh unification variable. Rule a-ktt-foralli checksa polymorphic type. We assign a unification variable as the kind of a, bring α
b : ?, a : α
b intoscope to check the body against ?, yielding the output context ∆2 , a : αb, ∆3 . As a goes out of
the scope in the conclusion, we need to drop a in the concluding context. To make sure thatdropping a outputs a well-formed context, we substitute away all solved unification variablesin ∆3 for the return kind, and keep only unsolved (∆3 ), which are ensured (∆3 ,→ a) tohave no dependency on a.
In the algorithmic elaborated kinding judgment ∆ ⊩ela µ : η, we keep the invariant:[∆]η = η. That is why in rule a-ela-app we substitute a with [∆]ρ2 .
Instantiation (⊩inst ) contains the only entry to unification (rule a-inst-refl).

7.7.4 Unification
The judgments of unification and promotion are excerpted in Figure 7.15. Most rules arenatural extensions of those in Haskell98.
Promotion The promotion judgment ∆ `prαb ω1 ⇝ ω2 a Θ is extended with kind annotations for unification variables. As our unification variables have kinds now, rule a-prb so that βb1 : ρ1 in the context is well-formed.
kuvarR-tt must also promote the kind of β,175

7 Kind Inference for Datatypes∆ ⊩inst µ1 : η <: ω ; µ2 a Θ(Instantiation)
a-inst-forall

a-inst-refl∆ ⊩u ω1 ≈ ω2 a Θ∆, αb : ω1 ⊩inst µ1 @b
α : η[a 7→ αb] <: ω2 ; µ2 a Θ∆ ⊩inst µ : ω1 <: ω2 ; µ a Θ∆ ⊩inst µ1 : ∀a : ω1 . η <: ω2 ; µ2 a Θ∆ ⊩kc σ ⇐ ω ; µ a Θ(Kind Checking)a-kc-sub∆ ⊩k σ : η ; µ1 a ∆1

∆1 ⊩inst µ1 : [∆1 ]η <: [∆1 ]ω ; µ2 a ∆2

∆ ⊩kc σ ⇐ ω ; µ2 a ∆2
∆ ⊩k σ : η ; µ a Θ(Kinding)
a-ktt-star∆ ⊩k ? : ? ; ? a ∆a-ktt-app∆ ⊩k τ1 : η1 ; ρ1 a ∆1

∆1 ⊩kapp (ρ1 : [∆1 ]η1 ) • τ2 : ω ; ρ a Θ∆ ⊩k τ1 τ2 : ω ; ρ a Θ
a-ktt-foralli∆, αb : ?, a : αb ⊩kc σ ⇐ ? ; µ a ∆2 , a : α
b , ∆3∆3 ,→ ak∆ ⊩ ∀a. σ : ? ; ∀a : αb. [∆3 ]µ a ∆2 , unsolved (∆3 )∆ ⊩kapp (ρ1 : η) • τ : ω ; ρ2 a Θ(Application Kinding)a-kapp-tt-arrow∆ ⊩kc τ ⇐ ω1 ; ρ2 a Θ∆ ⊩kapp (ρ1 : ω1 → ω2 ) • τ : ω2 ; ρ1 ρ2 a Θ
a-kapp-tt-forall∆, αb : ω1 ⊩kapp (ρ1 @b
α : η[a 7→ αb]) • τ : ω ; ρ a Θ∆ ⊩kapp (ρ1 : ∀a : ω1 . η) • τ : ω ; ρ a Θ

a-kapp-tt-kuvar∆1 , αb1 : ?, αb2 : ?, αb : ω = (bα1 → αb2 ), ∆2 ⊩kc τ ⇐ αb1 ; ρ2 a Θ∆1 , αb : ω, ∆2 ⊩kapp (ρ1 : αb) • τ : αb2 ; ρ1 ρ2 a Θ∆ ⊩ela µ : ηa-ela-app(Elaborated Kinding)∆ ⊩ela ρ1 : ω1 → ω2

∆ ⊩ela ρ2 : ω1

∆ ⊩ela ρ1 ρ2 : ω2a-ela-kapp∆ ⊩ela ρ1 : ∀a : ω. η

∆ ⊩ela ρ2 : ω∆ ⊩ela ρ1 @ρ2 : η[a 7→ [∆]ρ2 ]176
Figure 7.14: Selected rules for algorithmic kinding in PolyKinds

7.7 Kind Inference for PolyKinds∆ ⊩u ω1 ≈ ω2 a Θ(Unification)a-u-appa-u-refl-tt∆ ⊩u ρ1 ≈ ρ3 a ∆1

∆ ⊩u ω ≈ ω a ∆∆1 ⊩u [∆1 ]ρ2 ≈ [∆1 ]ρ4 a Θ∆ ⊩u ρ1 ρ2 ≈ ρ3 ρ4 a Θ

a-u-kvarL-tt∆ `prb : ω1 , Θ 2
αb ρ1 ⇝ ρ2 a Θ1 , αΘ1 ⊩ela ρ2 : ω2

Θ1 ⊩u [Θ1 ]ω1 ≈ ω2 a Θ3

∆ ⊩u αb ≈ ρ1 a Θ3 , αb : ω 1 = ρ2 , Θ 2

a-u-kvarL-lo-tt∆1 , ∆2 ++mv α
b : ω1 ; ΘΘ1 , {Θ2 } ⊩ela ρ2 : ω2∆[{Θ}] `prb : ω1 , Θ3 }, Θ4αb ρ1 ⇝ ρ2 a Θ1 , {Θ2 , α
Θ1 , {Θ2 } ⊩u [Θ1 , Θ2 ]ω1 ≈ ω2 a Θ5 , {Θ6 }∆[{∆1 , αb : ω1 , ∆2 }] ⊩u αb ≈ ρ1 a Θ5 , {Θ6 , αb : ω1 = ρ2 , Θ3 }, Θ4∆ `prαb ω1 ⇝ ω2 a Θ(Promotion)a-pr-kuvarR-tt

a-pr-tvar∆[a][bα] `prα]αb a ⇝ a a ∆[a][b∆ `prα][βb : ρ]αb [∆]ρ ⇝ ρ1 a Θ[b∆[bα][βb : ρ] `pr βb ⇝ βb1 a Θ[βb1 : ρ1 , αb][βb : ρ = βb1 ]αb∆1 ++mv ∆2 ; Θ
a-mv-empty(Moving)a-mv-kuvarvar(ω) ] dom(∆2 )• ++mv ∆ ; ∆a-mv-kuvarM∆1 ++mv ∆2 ; Θαb : ω, ∆1 ++mv ∆2 ; αb : ω, Θ¬(var(ω) ] dom(∆2 ))∆1 ++mv ∆2 , αb:ω;Θ

αb : ω, ∆1 ++mv ∆ ; ΘFigure 7.15: Selected rules for unification, promotion, and moving in PolyKinds

177

7 Kind Inference for Datatypes
Promotion now has a new failure mode: it cannot move proper quantified type variables. In
rule a-pr-tvar, the variable a must be to the left of αb.
Unfortunately, now we cannot easily tell whether promoting is terminating. In particular,
the convergence of promotion in Haskell98 is built upon the obvious fact that the size of thekind being promoted always gets smaller from the conclusion to the hypothesis. However,rule a-pr-kuvarR-tt breaks this invariant, as the judgment recurs into the kinds of unification variables, and the size of the kinds may be larger than the unification variables. As
shown in Section 7.7.5, we prove that promotion is terminating.
Unification The unification judgment ∆ ⊩u ω1 ≈ ω2 a Θ for PolyKinds features heterogeneous constraints. Recall the definition of X and Y discussed in Section 7.2.2. When
unifying αb βb with Maybe Bool, setting αb =Maybe and βb =Bool results in ill-kinded results. This suggests that when solving a unification variable, we need to first unify the kindsof both sides, as shown in rule a-u-kvarL-tt. When unifying α
b with ρ1 , we first promoteρ1 , yielding ρ2 . Now ρ2 must be well-formed under Θ1 , so we can get its kind ω1 . We thenunify the kinds of both sides. If everything succeeds, we set αb : ω1 = ρ2 . Under this rule,b
the unification αb β ≈Maybe Bool would be rejected correctly.
Rule a-u-kvarL-lo-tt is essentially the same as rule a-u-kvarL-tt, but deals with unification variables in a local scope. We thus need an extra step to move α
b towards the end of
the local scope.
Local scopes and moving As we have mentioned, a local scope can be reordered as longas the context is well-formed. Consider unifying {bα : ?, a : ?, b : αb, c : ?} ` αb ≈ a.
We see that a is not well-formed under the context before αb, and thus we cannot rewriteαb : ? with αb = a : ?. However, we can reorder the context to put α
b to the right of a. In
fact, to maximize the prefix context of αb, we can move α
b to the end of the context, yielding{a : ?, c : ?, αb : ?, b : αb}. As b depends on αb, b is also moved to the end of the context. The
final context is now {a : ?, c : ?, αb : ? = a, b : αb}.
The moving judgment ∆1 ++mv ∆2 ; Θ reorders the context, by appending ∆2 to theend of ∆1 , yielding Θ. As we have emphasized, reordering must preserve a well-formed
context. Therefore, every term that depends on ∆2 (rule a-mv-kuvarM) needs to be placedat the end, along with ∆2 .
In rule a-u-kvarL-lo-tt, we begin by reordering the local scope to put αb as far to theright as possible. The rest of the rule is essentially the same as rule a-u-kvarL-tt: the addedcomplication stems from the need to keep track of what bindings in the context are a part ofthe current local scope.

1787.7 Kind Inference for PolyKinds

7.7.5 Termination
Now the challenge is to prove that our unification algorithm terminates, which relies on theconvergence of the promotion algorithm. Next, we first discuss the termination of unification, and then move to the more complicated proof for promotion. Let h∆i denote thenumber of unsolved unification variables in ∆.
Lemma 7.6 (Promotion Preserves h∆i). If ∆ `prαb ω1 ⇝ ω2 a Θ, then h∆i

=

hΘi.

Lemma 7.7 (Unification Makes Progress). If ∆ ⊩u ω1 ≈ ω2 a Θ, then either Θ = ∆, orhΘi < h∆i.
Now we measure unification ∆ ⊩u ω1 ≈ ω2 a Θ using the lexicographic order of thepair (h∆i, |ω1 |), where |ω1 | computes the standard size of ω1 . We prove the pair always getssmaller from the conclusion to the hypothesis. Formally, assuming promotion terminates,
we have
Theorem 7.8 (Unification Terminates). Given a context ∆ ok, and kinds ρ1 and ρ2 , where[∆]ρ1 = ρ1 , and [∆]ρ2 = ρ2 , it is decidable whether there exists Θ such that ∆ ⊩u ρ1 ≈ ρ2 a
Θ.
We are not yet done, since Theorem 7.8 depends on the convergence of promotion. As
observed in rule a-pr-kuvarR, the size of the type being promoted increases from the conclusion to the hypothesis. Worse, the context never decreases. How do we prove promotion
terminates? The crucial observation for rule a-pr-kuvarR is that, when we move from theconclusion to the hypothesis, we also move from a unification variable to its kind. Since
the kind is well-formed under the prefix context of the variable, we are somehow moving
leftward in the context.
To formalize the observation, we define the dependency graph of a context.
Definition 21 (Dependency Graph). The dependency graph of a context ∆ is a directed graphwhere:
1. Nodes are all type variables and unsolved unification variables of ∆, and the terminal
symbols ?, → and Int.
2. Edges indicate the dependency from a type to its substituted kind. For example, ifαb : ω, then there is a directed edge from α
b to all the nodes appearing in [∆]ω.
As an illustration, consider the context ∆ = α
b : ?, αb1 : ?, αb2 : ? = α
b1 , αb3 : ? → αb2 ,whose dependency graph is given in Figure 7.16a (the reader is advised to ignore the color

179

7 Kind Inference for Datatypes
α
b3
α
b1

α
b

?

Int

→

(a) Promote α
b3 Int

→

βb2

α
b3

α
b3
α
b1

α
b

?

Int

(b) Promote ⋆ → α
b1

→

βb1

α
b

?

Int

(c) Solve α
b1 = βb1

→

βb1

α
b

?

Int

(d) Solve αb3 = βb2Θ1 = βb1 : ? , α
b : ?, αb1 : ? = βb1 , αb2 : ? = α
b1 , αb3 : ? → αb2
Θ2 = βb1 : ?, βb2 : ? → βb1 , α
b : ?, αb1 : ? = βb1 , αb2 : ? = α
b1 , αb3 : ? → α
b2 = βb2
···

∆ `pr
α
b ?⇝?a∆a-pr-kuvarR∆ `pr αb1 ⇝ βb1 a Θ1 7.16c
α
b

7.16b

∆ `pr
b1 ⇝ ? → βb1 a Θ1
αb ?→α∆ `pr αb3 ⇝ βb2 a Θ2 7.16d
α
b

7.16a

a-pr-appa-pr-kuvarR∆ `pr
b3 Int ⇝ βb2 Int a Θ2
α
b α

···

a-pr-app

Figure 7.16: Example of dependency graphfor now). There are several notable properties. First, as long as the context is well-formed,
the graph is acyclic except for the self-loop of ? and →. Second, solved unification variables
never appear in the graph. The kind of αb3 depends on α
b2 , which is already solved by α
b1 , sothe dependency goes from α
b3 to αb1 .
Now let us consider how promotion works in terms of the dependency graph, by trying
to unify ∆ ` αb≈αb3 Int. We start by promoting α
b3 Int. The derivation of the promotion is
given at the bottom of Figure 7.16. We omit some details via (· · · ) as promoting constants(?,→ and Int) is trivial. At the top of Figure 7.16 we give the dependency graph at certain
points in the derivation, where the part being promoted is highlighted in gray. At the beginning we are at Figure 7.16a. For αb3 , by rule a-pr-kuvarR, we first promote the kind ofαb3 , which is (after context application) ? → α
b1 (Figure 7.16b). As ? and → are always wellformed, we then promote α
b1 whose kind is the well-formed ?. Now we create a fresh variablebβ1 : ?, and solve α
b1 with βb1 (Figure 7.16c). Note since α
b1 is solved, the dependency fromαb3 goes to βb1 . Finally, we create a fresh variable βb2 with kind ? → βb1 , and solve α
b3 withbbb = β2 Int.
β2 (Figure 7.16d). Going back to unification, we solve α
We have several key observations. First, when we move from Figure 7.16a to Figure 7.16b

via rule a-pr-kuvarR, we are actually moving from the current node (bα3 ) to its adjacent180

7.7 Kind Inference for PolyKinds
nodes (?, →, and αb1 ). In other words, we are going down in this graph. Moreover, promotion
terminates immediately at type constants, so we never fall into the trap of loop. Further,when we solve variables with fresh ones (Figure 7.16c and Figure 7.16d), the shape of thegraph never changes.
With all those in mind, we conclude that the promotion process goes top-down via rule apr-kuvarR in the dependency graph until it terminates at types that are already well-formed.
Based on this conclusion, we can formally prove that promotion terminates.
Theorem 7.9 (Promotion Terminates). Given a context ∆[bα] ok, and a kind ω1 with [∆]ω1 =
ω1 , it is decidable whether there exists Θ such that ∆ `prαb ω1 ⇝ ω2 a Θ.

7.7.6 Soundness, Completeness and Principality
We prove our algorithm is sound:
Theorem 7.10 (Soundness of ⊩pgm ). If Ω; Γ ⊩pgm pgm : µ, then [Ω]Ω; [Ω]Γ `pgm pgm : [Ω]µ.
Unfortunately, we lose completeness, as the declarative system lacks principal kinds. Recall
the example in Section 7.7.2. This definition of T is rejected by the algorithmic quantification
check as the kind of d cannot be determined. However, the declarative system can guesscorrectly, e.g., Proxy b or Proxy c. Unfortunately, different choices lead to incomparable
kinds for T . Thus we argue such programs must be rejected.
Nevertheless, if the user explicitly writes down d :: Proxy b or d :: Proxy c, then the
program will be accepted by the algorithm. Thus, we conjecture that if all local dependencies
are annotated by the user, we can regain completeness. This, however, is a bit annoying tousers, because it means that we cannot accept definitions like the one below, even though thedependency is clear.
data Eq :: ∀k. k → k → ?
data P :: ∀k (a :: k) b. Eq a b → ?
We do not consider the incompleteness as a problematic issue in practice, as this scenario
is quite contrived and (we expect) will rarely occur “in the wild”. See more discussion of thispoint in Section 8.7.
Although the algorithm is incomplete, we offer the following guarantee: if the algorithm
accepts a definition, then that definition has a principal kind, and the algorithm infers the principal kind. To properly state the guarantee, we first define the notion of kind instantiation andpartial order of kinds:181

7 Kind Inference for Datatypes
Definition 22 (Kind Instantiation). Under context Σ, a kind η = ∀{φ1 }.∀φ2 . ω1 , where φ’s
can be empty, instantiates to ω, denoted as Σ ` η <: ω, if ω1 [φ1 7→ ρ1 ][φ2 7→ ρ2 ] = ω forsome ρ1 and ρ2 .
The relation is embedded in Σ `inst µ1 : η <: ω ; µ2 (Figure 7.9), where we ignore µ1
and µ2 .
Definition 23 (Partial Order of Kinds in PolyKinds). Under context Σ, a kind η1 is moregeneral than η2 , denoted as Σ ` η1  η2 , if for all ω such that Σ ` η2 <: ω, we haveΣ ` η1 <: ω.
To understand the definition, consider that if the program type-checks under T : η2 , thenit must type-check under T : η1 , as T : η1 can be instantiated to all monokinds that T : η2
is used at.
Now we lift the definition of ⊩grp to be the generalized result of kinds and contexts. And
we are ready now to formally prove our guarantee of principality:ii

Theorem 7.11 (Principality of ⊩grp ). If Ω ⊩grp rec Ti ; ηi i ; Γi , then whenever [Ω]Ω `grpi

i

i

rec Ti ; ηi′ ; Ψi holds, we have [Ω]Ω ` [Ω]ηi  ηi′ .
This result echoes the result in the term-level type inference algorithm for Haskell ([Vytiniotis et al. 2011, Section 6.5]): our algorithm does not infer every kind acceptable by the
declarative system, but the kinds it does infer are always the best (principal) ones.

7.8 Language Extensions
We have seen that the PolyKinds system incorporates many features and enjoys desirable
properties. In this section, we discuss how the PolyKinds system can be extended with more
related language features. Appendix C.1 contains a few more, less impactful extensions.

7.8.1 Higher-Rank Polymorphism
The system can be extended naturally to support higher-rank polymorphism [Dunfield andKrishnaswami 2013; Peyton Jones et al. 2007]. With higher-rank polymorphism, every type
can have a polymorphic kind. For example, data constructor declarations become ∀φ. D σi iinstead of ∀φ. D τi i .
Unfortunately, higher-rank polymorphism breaks principality. Consider:data Q1 :: ∀k 1 k 2 . k 1 → ?
data Q2 :: (∀(k 1 : ?) (k 2 : k 1 ). k 1 → ?) → ?

182

7.8 Language Extensions
First, we modify the definition of partial order of kinds (Definition 23) to state that one
kind is more general than another if it can be instantiated to all polykinds that the otherkind can be instantiated to. Now consider the kind of Q1, which under the algorithm isgeneralized to ∀{k3 : ?} (k 1 : ?) (k 2 : k3). k 1 → ?. In Theorem 7.11, we guarantee that
this kind is a principal kind as it can be instantiated to all monokinds that other possible
kinds for Q1, e.g., ∀ (k 1 :: ?) (k 2 :: k 1 ). k 1 → ?, can be instantiated to. However, under
the new definition, ∀{k3 :: ?} (k 1 :: ?) (k 2 :: k3). k 1 → ? is no longer more general than∀(k 1 :: ?) (k 2 :: k 1 ). k 1 → ?, as there is no way to instantiate the former to the latter. To see
why we need to modify the definition at all, consider the rank-2 kind of Q2, which expects
exactly an argument of kind ∀(k 1 :: ?) (k 2 :: k 1 ). k 1 → ?.
We do not consider the absence of principality in the setting of higher-rank polymorphism
to be a severe issue in practice, for two reasons: to our knowledge, higher-rank polymorphism for datatypes is not heavily used; and it may be possible to recover principality throughthe use of a more generous type-subsumption relation. Currently, GHC (and our model ofit) does not support first-class type-level abstraction (i.e., Λ in types) [Jones 1995]. The lackof lambda type-level abstractions largely simplifies the kind inference algorithm (e.g., it allows us to decompose type applications as in rule a-u-app). However, it also means that
we cannot introduce new variables (also called skolemization [Peyton Jones et al. 2007, Section 4.6.2]) in an attempt to equate one type with another. Returning to the example above,
we could massage ∀{k3 :: ?} (k 1 :: ?) (k 2 :: k3). k 1 → ? to ∀(k 1 :: ?) (k 2 :: k 1 ). k 1 → ? if
we could abstract over the k 1 in the target type. Recent advances in type-level programmingin Haskell [Kiss et al. 2019] suggest we may be able to add first-class abstraction, meaningthat type-subsumption can use both instantiation and skolemization. We conjecture that
this development would recover principal types.

7.8.2 Generalized Algebraic Datatypes (GADTs)
The focus of this work has been on uniform datatypes, where every constructor’s type matches
exactly the datatype head: this fact allows us to easily choose the subscript to the `dc judgment in, e.g., rule dt-tt. Programmers in modern Haskell, however, often use generalized
algebraic datatypes [Peyton Jones et al. 2006; Xi et al. 2003]. There are two impacts of adding
these, both of which we found surprising.

Equality constraints The power of GADTs arises from how they encode local equality
constraints. Any GADT can be rewritten to a uniform datatype with equality constraints [Vytiniotis et al. 2011, Section 4.1]. For example, we can rewrite183

7 Kind Inference for Datatypes
data G a where
MkG :: G Bool
to bedata G a = (a ∼ Bool) ⇒ MkG
where ∼ describes an equality constraint. For our purposes of doing kind inference, theseequality constraints are uninteresting: the ∼ operator simply relates two types of the samekind and can be processed as any polykinded type constructor would be. Modeling constraints to the left of a ⇒ similarly would add a little clutter to our rules, but would offer noreal challenges.
The unexpected simplicity of adding GADTs to our system arises from a key fact: we do
not ever allow pattern-matching. A GADT pattern-match brings a local equality assumption
into scope, which would influence the unification algorithm. However, as pattern matchingdoes not happen in the context of datatype declarations, we avoid this wrinkle here.
Syntax The implementation of GADTs in GHC has an unusual syntax:
data G a where
MkG :: a → G Int
The surprising aspect of this syntax is that the two as above are different: the a in the header
is unrelated to the a in the data constructor. This seemingly inconsequential design choicemakes kind inference for GADTs very challenging, as constructors have no way to refer backto the datatype parameters. Given that this aspect of GADTs is a quirk of GHC’s design—andis not repeated in other languages that support GADTs—we remark here that it is odd andperhaps should be remedied. We will return back to this discussion in Section 9.4.

7.8.3 Type Families
Type families [Chakravarty et al. 2005] are, effectively, type-level functions. Kind inferenceof type families thus can be designed much like type inference for ordinary functions. However, as they can have dependency, the complications we describe in this paper would arise
here, too. In particular, unification would have to be kind-directed, as we have described.
The current syntax for closed type families [Eisenberg et al. 2014] shares the same scoping
problem as the syntax for GADTs, so our arguments above apply to closed type families
equally.

184

7.8 Language Extensions
The challenge with type families is that they indeed do pattern-matching, and thus (inconcert with GADTs) can bring local equalities into scope. A full analysis of the ramifications
here is beyond the scope of this paper, but we believe the literature on type inference in thepresence of local equalities would be helpful. Principal among these is the work of Vytiniotis
et al. [2011], but Gundry [2013] and Eisenberg [2016] also approach this problem in thecontext of dependent types.

185

Part V
Epilogue187

8 Related Work
There is a great deal of work related to this thesis. Along the way we have discussed some of
the most relevant work. In this chapter, we briefly review more related work.

8.1 Type Inference for Higher-Rank Types
Predicative higher-rank type inference. Odersky and Läufer [1996] introduced atype system for higher-rank implicit polymorphic types. Based on that, Peyton Jones et al.
[2007] developed an approach for type inference for higher-rank types using traditional bidirectional type checking. They use a more general subtyping relation, inspired by the typecontainment relation by Mitchell [1988], which supports deep skolemisation. With deep
skolemisation, examples like ∀a. Int → Int <: Int → ∀a. a are allowed. We believe deep
skolemisation is compatible with our subtyping definition (i.e., our subtyping can naturally
integrate deep skolemisation). Note, though, recently GHC has resiled from deep skolemisation [Peyton Jones 2020], as it is believed that the cost of deep skolemisation is large compared to its benefit.
Dunfield and Krishnaswami [2013] build a simple and concise algorithm for higher-rankpolymorphism based on traditional bidirectional type checking. They deal with the samelanguage of Peyton Jones et al. [2007], except they do not have let expressions nor generalization (though it is discussed in design variations). Built upon some of these techniques,Dunfield and Krishnaswami [2019] extend the system to a much richer type language that
includes existentials, indexed types, and equations over type variables.
Impredicative higher-rank type inference. While our work focuses on predicative
higher-rank types, there are also a lot of work on type inference for impredicative higherrank types. Many of these work replies on new forms of types. MLF [Le Botlan and Rémy
2003, 2009; Rémy and Yakobowski 2008] generalizes ML with first-class polymorphism. MLF
introduces a new type of bounded quantification (either rigid or flexible) for polymorphic
types so that instantiation of polymorphic bindings is delayed until a principal type is found.
higher-rank types. The HML system [Leijen 2009] is proposed as a simplification and restric-

189

8 Related Worktion of MLF . HML only uses flexible types, which simplifies the type inference algorithm,
but retains many interesting properties and features.
The FPH system [Vytiniotis et al. 2008] introduces boxy monotypes into System F types.
One critique of boxy type inference is that the impredicativity is deeply hidden in the algorithmic type inference rules, which makes it hard to understand the interaction between its
predicative constraints and impredicative instantiations [Rémy 2005].
Recently, Serrano et al. [2020, 2018] exploit impredicative instantiations of type variables
that appears under a type constructor (i.e., type variables are guarded) Serrano et al. [2018]is strictly subsumed by Serrano et al. [2020]. The former distinguishes variables using threesorts, so that certain sorts of variables can be instantiated with higher-rank polymorphic
types, while the latter inspects the function arguments and assigns impredicative instantiations before monomorphic ones.

8.2 Bidirectional Type Checking
Bidirectional type checking was popularized by the work of Pierce and Turner [2000]. It has
since been applied to many type systems with advanced features. The alternative application
mode introduced in Chapter 3 enables a variant of bidirectional type checking. There aremany other efforts to refine bidirectional type checking.
Colored local type inference [Odersky et al. 2001] allows partial type information to be
propagated, by distinguishing inherited types (known from the context) and synthesized
types (inferred from terms). A similar distinction is achieved in Dunfield and Krishnaswami[2013] by manipulating type variables.
Tridirectional type checking [Dunfield and Pfenning 2004] is based on bidirectional typechecking and has a rich set of property types including intersections, unions and quantified
dependent types, but without parametric polymorphism. Tridirectional type checking has a
new direction for supporting type checking unions and existential quantification.
Greedy bidirectional polymorphism [Dunfield 2009] adopts a greedy idea from Cardelli[1993] on bidirectional type checking with higher-rank types, where type variables in instantiations are determined by their first constraint. In this way, they support some uses ofimpredicative polymorphism. However, the greediness also makes many obvious programs
rejected.
A detailed survey of the development of bidirectional type checking is given by Dunfield
and Krishnaswami [2020], which collect and explain the design principles of bidirectional
type checking, and summarize past research related to bidirectional type checking.

190

8.3 Gradual Typing

8.3 Gradual Typing
The seminal paper by Siek and Taha [2006] is the first to propose gradual typing, which enables programmers to mix static and dynamic typing in a program by providing a mechanism
to control which parts of a program are statically checked. The original proposal extends thesimply typed lambda calculus by introducing the unknown type ? and replacing type equality
with type consistency. Casts are introduced to mediate between statically and dynamically
typed code. Later Siek and Taha [2007] incorporated gradual typing into a simple object oriented language, and showed that subtyping and consistency are orthogonal – an insight thatpartly inspired our work on GPC. We show that subtyping and consistency are orthogonal
in a much richer type system with higher-rank polymorphism. Siek et al. [2009] explores
the design space of different dynamic semantics for simply typed lambda calculus with casts
and unknown types. In the light of the ever-growing popularity of gradual typing, and its
somewhat murky theoretical foundations, Siek et al. [2015] felt the urge to have a complete
formal characterization of what it means to be gradually typed. They proposed a set of criteria that provides important guidelines for designers of gradually typed languages. Cimini
and Siek [2016] introduced the Gradualizer, a general methodology for generating gradualtype systems from static type systems. Later they also develop an algorithm to generate dynamic semantics [Cimini and Siek 2017]. Garcia et al. [2016] introduced the AGT approachbased on abstract interpretation. As we discussed, none of these approaches instructed us
how to define consistent subtyping for polymorphic types.
There is some work on integrating gradual typing with rich type disciplines. Bañados
Schwerter et al. [2014] establish a framework to combine gradual typing and effects, withwhich a static effect system can be transformed to a dynamic effect system or any intermediate blend. Jafery and Dunfield [2017] present a type system with gradual sums, which
combines refinement and imprecision. We have discussed the interesting definition of directed consistency in Section 4.2. Castagna and Lanvin [2017] develop a gradual type systemwith intersection and union types, with consistent subtyping defined by following the idea of
Garcia et al. [2016]. Eremondi et al. [2019] develop a gradual dependently-typed language,
where compile-time normalization and run-time execution are distinguished to account for
nontermination and failure. TypeScript [Bierman et al. 2014] has a distinguished dynamic
type, written any, whose fundamental feature is that any type can be implicitly converted toand from any. Our treatment of the unknown type in Figure 4.6 is similar to their treatmentof any. However, their type system does not have polymorphic types. Also, unlike our consistent subtyping which inserts runtime casts, in TypeScript, type information is erased after
compilation so there are no runtime casts, which makes runtime type errors possible.

191

8 Related Work

8.4 Gradual Type Systems with Explicit Polymorphism

Morris [1973] dynamically enforces parametric polymorphism and uses sealing functions as
the dynamic type mechanism. More recent works on integrating gradual typing with parametric polymorphism include the dynamic type of Abadi et al. [1995] and the Sage languageof Gronski et al. [2006]. None of these has carefully studied the interaction between staticallyand dynamically typed code.
Ahmed et al. [2009] proposed λB that extends the blame calculus [Wadler and Findler
2009] to incorporate polymorphism. The key novelty of their work is to use dynamic sealing to enforce parametricity. As such, they end up with a sophisticated dynamic semantics. Later, Ahmed et al. [2017] prove that with more restrictions, λB satisfies parametricity.
Compared to their work, our GPC type system can catch more errors earlier since, as we
argued, their notion of compatibility is too permissive. For example, the following is rejected(more precisely, the corresponding source program never gets elaborated) by our type system:(λx : ?. x + 1) : ∀a. a → a ⇝ h? → Int ,→ ∀a. a → ai(λx : ?. x + 1)
while the type system of λB would accept the translation, though at runtime, the program
would result in a cast error as it violates parametricity. We emphasize that it is the combination of our powerful type system together with the powerful dynamic semantics of λB thatmakes it possible to have implicit higher-rank polymorphism in a gradually typed setting.
Devriese et al. [2017] proved that embedding of System F terms into λB is not fully abstract.
Igarashi et al. [2017] also studied integrating gradual typing with parametric polymorphism.
They proposed System FG , a gradually typed extension of System F, and System FC , a newpolymorphic blame calculus. As has been discussed extensively, their definition of type consistency does not apply to our setting (implicit polymorphism). All of these approaches mix
consistency with subtyping to some extent, which we argue should be orthogonal. On a side
note, it seems that our calculus can also be safely translated to System FC . However we do
not understand all the tradeoffs involved in the choice between λB and System FC as a target.
Recently, Toro et al. [2019] applied AGT to designing a gradual language with explicit
parametric polymorphism, claiming that graduality and parametricity are inherently incompatible. However, later New et al. [2019] show that by modifying System F’s syntax to makethe sealing visible, both graduality and parametricity can be achieved.

192

8.5 Gradual Type Inference

8.5 Gradual Type Inference
Siek and Vachharajani [2008] studied unification-based type inference for gradual typing,where they show why three straightforward approaches fail to meet their design goals. Oneof their main observations is that simply ignoring dynamic types during unification does
not work. Therefore, their type system assigns unknown types to type variables and infers
gradual types, which results in a complicated type system and inference algorithm. In ouralgorithm presented in Chapter 5, comparisons between existential variables and unknown
types are emphasized by the distinction between static existential variables and gradual existential variables. By syntactically refining unsolved gradual existential variables with unknown types, we gain a similar effect as assigning unknown types, while keeping the algorithm relatively simple. Garcia and Cimini [2015] presented a new approach where gradualtype inference only produces static types, which is adopted in our type system. They also
deal with let-polymorphism (rank 1 types). They proposed the distinction between staticand gradual type parameters, which inspired our extension to restore the dynamic gradualguarantee. Although those existing works all involve gradual types and inference, none ofthese works deal with higher-rank implicit polymorphism.

8.6 Haskell and GHC
The Glasgow Haskell Compiler. The systems we present in Chapter 7 are inspired by
the algorithms implemented in GHC. However, our goal in the design of these systems is to
produce a sound and (nearly) complete pair of specification and implementation, not simply
to faithfully record what is implemented. We have identified ways that the GHC implementation can improve in the future. For example, GHC quantifies over local scopes as specified
where we believe they should be inferred; and the tight connection in our system between
unification and promotion may improve upon GHC’s approach, which separates the two.
The details of the relationship between our work and GHC (including a myriad of ways ourdesign choices differ in small ways from GHC’s) appear in Appendix C.2.
Type inference in Haskell. Type inference in Haskell is inspired by Damas and Milner[1982] and Pottier and Rémy [2005], extended with various type features, including higher
rank polymorphism [Peyton Jones et al. 2007] and local assumptions [Schrijvers et al. 2009;
Simonet and Pottier 2007; Vytiniotis et al. 2011], among others. However, none of theseworks describe an inference algorithm for datatypes, nor do they formalize type variables ofvarying kinds or polymorphic recursion.

193

8 Related Work
Dependent Haskell. Our PolyKinds system merges types and kinds, a key feature of Dependent Haskell (DH) [Eisenberg 2016; Gundry 2013; Weirich et al. 2013, 2017]. There isongoing work dedicated to its implementation [Xie and Eisenberg 2018]. The most recent
work by Weirich et al. [2019] integrates roles Breitner et al. [2016] with dependent types.
Our work is the first presentation of unification for DH, and our system may be useful indesigning DH’s term-level type inference.
Polymorphic recursion.

Mycroft [1984] presented a semi-algorithm for polymorphic

recursion. Jim [1996] and Damiani [2003] studied typing rules for recursive definitions basedon rank-2 intersection types. Comini et al. [2008] studied recursive definitions in a typesystem that corresponds to the abstract interpreter in Gori and Levi [2002, 2003]. Our system
PolyKinds does not infer polymorphic recursion; instead, we exploit kind annotations toguide the acceptance of polymorphic recursion, following Jones [1999].
Constraint-solving approaches. Many systems (e.g. [Pottier and Rémy 2005]) adopt a
modular presentation of type inference, which consists of a constraint generator and a constraint solver. For simplicity, we have presented an eager unification algorithm instead ofusing a separate constraint solver. However, we believe changing to a constraint-solving approach should not change any of our main results. Xie et al. [2019b] considers this point
further.

8.7 Unification with dependent types
While full higher-order unification is undecidable [Goldfarb 1981], the pattern fragment [Miller
1991] is a well-known decidable fragment. Much literature [Abel and Pientka 2011; Gundry
and McBride 2013; Reed 2009] is built upon the pattern fragment.
Unification in a dependently typed language features heterogeneous constraints. To provecorrectness, Reed [2009] used a weaker invariant on homogeneous equality, typing modulo,
which states that two sides are well typed up to the equality of the constraint yet to be solved.
Gundry and McBride [2013] observed the same problem, and use twin variables to explicitly
represent the same variable at different types, where twin variables are eliminated once theheterogeneous constraint is solved. In both approaches the well-formedness of a constraint
depends on other constraints. Cockx et al. [2016] proposed a proof-relevant unification thatkeeps track of the dependencies between equations. Different from their approaches, ouralgorithm unifies the kinds when solving unification variables. This guarantees that our unification always outputs well-formed solutions.

194

8.7 Unification with dependent types
Ziliani and Sozeau [2015] present the higher-order unification algorithm for CIC, the baselogic of Coq. They favor syntactic equality by trying first-order unification, as they argue
the first-order solution gives the most natural solution. However, they omit a correctness
proof for their algorithm. Coen [2004] also considers first-order unification, but only thesoundness lemma is proved. Different from their systems, our system is based on the novel
promotion judgment, and correctness including soundness and termination is proved.
The technique of suspended substitutions [Eisenberg 2016; Gundry and McBride 2013]is widely adopted in unification algorithms. Our system provides a design alternative, ourquantification check. Choosing between suspended substitutions and the quantification checkis a user-facing language design decision, as suspended substitutions can accept some moreprograms. The quantification check means that the kind of a locally quantified variable amust be fully determined in a’s scope; it may not be influenced by usage sites of the construct that depends on a. Suspended substitutions relax this restriction. We conjecture thatsuspended substitutions can yield a complete algorithm. However, that mechanism is complex. Moreover, unification based on suspended substitutions is only decidable for the pattern fragment. Our system, in contrast, avoids all the complication introduced by suspended
substitutions through its quantification check. Our unification terminates for all inputs, preserving backward compatibility to Hindley-Milner-style inference. Although we reject thedefinition of T (Section 7.7.2), we can solve more constraints outside the pattern fragment.
We conjecture that those constraints are much more common than definitions like T . Suspended substitutions often come with a pruning process [Abel and Pientka 2011], which
produces a valid solution before solving a unification variable. Our promotion process has asimilar effect.
Homogeneous kind-preserving unification. Jones [1995] proposed a homogeneouskind-preserving unification between two types. Kinds κ are defined only as ? or κ1 → κ2 .
As the kind system is much simpler, kind-preserving unification ∼κ is simply subscriptedby the kind, and working out the kinds is straightforward. Our unification subsumes Jones’s
algorithm.
Context extension. Our approach of recording unification variables and their solutionsin the contexts is inspired by Gundry et al. [2010] and Dunfield and Krishnaswami [2013].
Gundry and McBride [2013] applied the approach to unification in dependent types, where
the context also records constraints; constraints also appear in context in Eisenberg [2016].
Further, in PolyKinds, we extend the context extension approach with local scopes, supporting groups of order-insensitive variables.

195

9 Summary and Future Directions
In summary, this dissertation has pushed the research on predicative implicit higher-rankpolymorphism further, and we believe that contributions in this dissertation can be used to
guide the continued evolution of (functional) programming language design and implementations. Specifically, with the new bidirectional type checking algorithm using the application mode, we were able to type-check programs that traditional type inference algorithms
cannot, and thus provide new insights for inference algorithm design with bidirectional typechecking. With the integration of higher-rank polymorphism and gradual typing, we provided a step forward in gradualizing modern functional programming languages like Haskell.
Moreover, the work on type promotion simplified type inference algorithms with tricky dependency and scoping issues, and the kind inference for datatypes presented a first known,
detailed account of datatypes, which can serve as a guide for future development of datatypes.
In this section we discuss some future directions we would like to pursue.

9.1 Dependent Type Systems with Application mode
The application mode is possibly applicable to systems with advanced features, where type
inference is sophisticated or even undecidable. One promising application is, for instance,
dependent type systems [Xi and Pfenning 1999]. Type systems with dependent types usuallyunify the syntax for terms and types, with a single lambda abstraction generalizing both typeand lambda abstractions. Unfortunately, this means that the let desugar is not valid in thosesystems. As a concrete example, consider desugaring the expression let a = Int in λx :a. x + 1 into (λa.λx : a. x + 1) Int, which is ill-typed because the type of x in the abstraction
body is a and not Int.
Because let cannot be encoded, declarations cannot be encoded either. Modeling declarations in dependently typed languages is a subtle matter, and normally requires some additional complexity [Severi and Poll 1994].
We believe that the same technique presented in Section 3.5.3 can be adapted into a dependently typed language to enable a let encoding. In a dependent type system with unified syntax for terms and types, we can combine the two forms in the typing context, i.e.,197

9 Summary and Future Directions
x : σ and a = σ, into a unified form x = e : σ. Then we can combine two application
rules rule ap-app-app and rule ap-app-tapp into rule ap-app-dapp, and also two abstraction rules rule ap-app-lam and rule ap-app-tlam into rule ap-app-dlam.
Ψ `AP e2 ⇒ σ1Ψ; Σ, e2 : σ1 `AP e1 ⇒ σ2Ψ; Σ `AP e1 e2 ⇒ σ2
Ψ, x = e1 : σ1 ; Σ `AP e ⇒ σ2Ψ; Σ, e1 : σ1 `AP λx. e ⇒ σ2

ap-app-dapp

ap-app-dlam

With such rules it would be possible to handle declarations easily in dependent type systems.

9.2 Type Inference for Intersection Type Systems
Another type system that could possibly benefit from the application mode is intersection
type systems [Coppo et al. 1979; Pottinger 1980; Salle 1978]. In particular, we consider intersection type systems with an explicit merge operator [Dunfield 2014]. In such a system,
we can construct terms of an intersection type, like 1 , , true of type Int & Bool. Thanks tosubtyping, a term of type Int & Bool can also be used as if it had type Int, or as if it had type
Bool. Calculi with disjoint intersection types [Alpuim et al. 2017; Bi et al. 2019; Oliveira et al.
2016] incorporate a coherent merge operator. In such calculi the merge operator can merge
two terms with arbitrary types as long as their types are disjoint; disjointness conflicts arereported as type-errors. As illustrated by Xie et al. [2020], the expressive power of disjoint
intersection types can encode diverse programming language features, promising an economy of theory and implementation.
Disjoint intersection types also pose challenges to type inference. Supposing that we havesucc : Int → Int and not : Bool → Bool, consider the following term:(succ , , not) 3
We expect the expression to type-check, as according to subtyping, the term (succ , , not)of type (Int → Int & Bool → Bool) can also be used as type Int → Int. Thus we expect
typing to automatically pick succ and apply it to 3. To this end, we need to push the type
information of the argument (3) into the function (succ , , not).
Future work is required to explore how well the application mode can be used for type
inference in intersection type systems, and whether it can be integrated with the distributivity
subtyping rules of intersection types [Bi et al. 2019].

198

9.3 Gradualizing Type Classes

9.3 Gradualizing Type Classes
In Section 4.1.2, we discussed about gradualizing modern functional programming languages
like Haskell. One of its core abstraction features in Haskell is type classes. Type classes[Wadler and Blott 1989] were initially introduced in Haskell to make ad-hoc overloading less
ad-hoc, and since then have been adopted in many languages including Mercury [Henderson
et al. 1996], Coq [Sozeau and Oury 2008], PureScript [Freeman 2017], and Lean [de Mouraet al. 2015]. An interesting future direction then is to gradualizing type classes.
Consider again the example used in Section 4.1.2:(\f. (f 1, f 'a')) (\x. x)

While f : ∀a. a → a is of course a valid type annotation, it unfortunately rules out many
valid arguments that may have type class constraints in their types, e.g.,show:: Show a ⇒ a → String(\f :: ∀a. a → a. (f 1, f 'a')) show-- rejectedWith gradual typing, if we annotation f with the the unknown type ?, we expect that thefollowing expression can type-check.
(\f :: ?. (f 1, f 'a')) show

However, a nontrivial challenge in gradualizing type classes is that the dynamic semantics of type classes is not expressed directly but rather by type-directed elaboration into a
simpler language without type classes. Thus the dynamic semantics of type classes is given
indirectly as the dynamic semantics of their elaborated forms. Consider show as an example. The dictionary-passing elaboration of type-classes translates the type of show into thefollowing one, supposing ShowD is the dictionary type of the type class show.
show :: ShowD a → a → String

Now with the unknown type, we cannot predict how to elaborate the original expression.
In particular, if f is applied to show, it means that f needs to be elaborated into a functionthat actually takes two arguments, first the dictionary and then the argument.
(\f. (f showInt 1, f showChar 'a')) show

This kind of uncertainty in elaboration brings extra complexity and may interact with
explicit casts in the target blame calculi.

199

9 Summary and Future Directions

9.4 Generalized Algebraic Datatypes (GADTs)
A natural extension of PolyKinds is to include GADTs. We have briefly discussed GADTs in
Section 7.8.2. In particular, we are interested in finding the right formalization of GADTs.
Haskell’s syntax for GADT declarations is quite troublesome. Consider these examples:data R a where
MkR :: b → R bdata S a where
MkS :: S bdata T a where
MkT :: ∀(k :: ?) (b :: k). T b
In GHC’s implementation of GADTs, any variables declared in the header (between data andwhere) do not scope. In all the examples above, the type variable a does not scope over theconstructor declarations. This is why we have written the variable b in those types, to makeit clear that b is distinct from a. We could have written a—it would still be a distinct a fromthat in the header—but it would be more confusing.
The question is: how do we determine the kind of the parameter to the datatype? One
possibility is to look only in the header. In all cases above, we would infer no constraints andwould give each type a kind of ∀(k :: ?). k → ?. This is unfortunate, as it would make R
a kind-indexed GADT: the MkR constructor would carry a proof that the kind of its typeparameter is ?. This, in turn, wreaks havoc with type inference, as it is hard to infer the resulttype of a pattern-match against a GADT Vytiniotis et al. [2011].
Furthermore, this approach might accept more programs than the user wants. Consider
this definition:
data P a where
MkP1 :: b → P b
MkP2 :: f a → P f
Does the user want a kind-indexed GADT, noting that b and f have different kinds? Or
would the user want this rejected? If we make the fully general kind ∀k. k → ? for P, thiswould be accepted, perhaps surprising users.
It thus seems we wish to look at the data constructors when inferring the kind of thedatatype. The challenge in looking at data constructors is that their variables are locally2009.4 Generalized Algebraic Datatypes (GADTs)
bound. In MkR and MkS, we implicitly quantify over b. In MkR, we discover that b :: ?, andthus that R must have kind ? → ?. In MkS, we find no constraints on b’s kind, and thus noconstraints on S’s argument’s kind, and so we can generalize to get S :: ∀(k :: ?). k → ?. Let
us now examine MkT : it explicitly brings k and b into scope. Thus, the argument to T has
b—with k,
local kind k. It would be impossible to unify the kind of T ’s argument—call it α
because k would be bound to the right of αb in an inference context. Thus it seems we would
reject T .
Our conclusion here is that the design of GADTs in GHC/Haskell is flawed: the type variables mentioned in the header should indeed scope over the constructors. This would mean
we could reject T : if the user wanted to explicitly make T polymorphically kinded, they
could do so right in the header. So one possible application of our work is to apply ourinsights in the scoping (order in the context) and unification into formalizing GADTs.

201

Bibliography[Citing pages are listed after each reference.]
Martin Abadi, Luca Cardelli, Benjamin Pierce, and Didier Rémy. 1995. Dynamic Typing in
Polymorphic Languages. Journal of Functional Programming 5, 1 (1995), 111–130. [cited
on page 192]
Andreas Abel and Brigitte Pientka. 2011. Higher-order dynamic pattern unification for dependent types and records. In International Conference on Typed Lambda Calculi and Applications. Springer, 10–26. [cited on pages 194 and 195]
Amal Ahmed, Robert Bruce Findler, Jacob Matthews, and Philip Wadler. 2009. Blame forAll. In Proceedings for the 1st Workshop on Script to Program Evolution (Genova, Italy)(STOP ’09). Association for Computing Machinery, New York, NY, USA, 1–13. https://doi.org/10.1145/1570506.1570507 [cited on pages 11, 68, 70, 80, 82, 85, 107,
and 192]
Amal Ahmed, Dustin Jamner, Jeremy G. Siek, and Philip Wadler. 2017. Theorems for Free
for Free: Parametricity, With and Without Types. In Proceedings of the 22nd International
Conference on Functional Programming. [cited on page 192]João Alpuim, Bruno C. d. S. Oliveira, and Zhiyuan Shi. 2017. Disjoint polymorphism. In
European Symposium on Programming (ESOP). [cited on page 198]
P. B. Andrews. 1971. Resolution in type Theory. Journal of Symbolic Logic 36 (1971), 414–432.
[cited on page 149]
Andrea Asperti, Wilmer Ricciotti, Claudio Sacerdoti Coen, and Enrico Tassi. 2012. A BiDirectional Refinement Algorithm for the Calculus of (Co) Inductive Constructions. Logical Methods in Computer Science 8 (2012), 1–49. [cited on page 24]
Felipe Bañados Schwerter, Ronald Garcia, and Éric Tanter. 2014. A Theory of Gradual Effect
Systems. In Proceedings of the 19th International Conference on Functional Programming.
[cited on pages 61 and 191]

203

Bibliography
Xuan Bi, Ningning Xie, Bruno C. d. S. Oliveira, and Tom Schrijvers. 2019. Distributive disjoint polymorphism for compositional programming. In European Symposium on Programming (ESOP). [cited on page 198]
Gavin Bierman, Martín Abadi, and Mads Torgersen. 2014. Understanding TypeScript. In
Proceedings of the 28th European Conference on Object-Oriented Programming. [cited on
pages 8, 61, and 191]
Gavin Bierman, Erik Meijer, and Mads Torgersen. 2010. Adding Dynamic Types to C#.
In Proceedings of the European Conference on Object-Oriented Programming. [cited on
page 61]
Richard S. Bird and Lambert Meertens. 1998.

Nested datatypes. In LNCS 1422: Pro-

ceedings of Mathematics of Program Construction, Johan Jeuring (Ed.). Springer-Verlag,
Marstrand, Sweden, 52–67.

http://www.cs.ox.ac.uk/people/richard.bird/

online/BirdMeertens98Nested.pdf [cited on page 150]
Ambrose Bonnaire-Sergeant, Rowan Davies, and Sam Tobin-Hochstadt. 2016. Practical Optional Types for Clojure. In Programming Languages and Systems. [cited on pages 8 and 61]
Joachim Breitner, Richard A Eisenberg, Simon Peyton Jones, and Stephanie Weirich. 2016.
Safe zero-cost coercions for Haskell. Journal of Functional Programming 26 (2016). [cited
on page 194]
L. Cardelli. 1986. A polymorphic lambda-calculus with Type:Type. Technical Report 10. SRC.
[cited on page 148]
Luca Cardelli. 1993. An implementation of FSub. Technical Report. Research Report 97,
Digital Equipment Corporation Systems Research Center. [cited on pages 89 and 190]
Giuseppe Castagna and Victor Lanvin. 2017. Gradual Typing with Union and Intersection
Types. Proc. ACM Program. Lang. 1, ICFP, Article 41 (Aug. 2017), 28 pages. [cited on
pages 61, 75, 99, and 191]
Manuel M. T. Chakravarty, Gabriele Keller, and Simon Peyton Jones. 2005. Associated typesynonyms. In Proceedings of the Tenth ACM SIGPLAN International Conference on Functional Programming (Tallinn, Estonia) (ICFP ’05). ACM, New York, NY, USA, 241–253.
https://doi.org/10.1145/1086365.1086397 [cited on page 184]
Gang Chen. 2003.

Coercive Subtyping for the Calculus of Constructions (POPL ’03).

10 pages. [cited on page 53]

204

Bibliography
Alonzo Church. 1941. The calculi of lambda-conversion. Number 6. Princeton University
Press. [cited on page 65]
Matteo Cimini and Jeremy G. Siek. 2016. The Gradualizer: A Methodology and Algorithm
for Generating Gradual Type Systems. In Proceedings of the 43rd Symposium on Principles
of Programming Languages. [cited on pages 61, 75, 83, and 191]
Matteo Cimini and Jeremy G. Siek. 2017. Automatically Generating the Dynamic Semantics of Gradually Typed Languages. In Proceedings of the 44th Symposium on Principles ofProgramming Languages. [cited on pages 61 and 191]
Jesper Cockx, Dominique Devriese, and Frank Piessens. 2016. Unifiers as equivalences:
proof-relevant unification of dependently typed data. In Proceedings of the 21st ACM
SIGPLAN International Conference on Functional Programming (Nara, Japan) (ICFP
2016). ACM, New York, NY, USA, 270–283. https://doi.org/10.1145/2951913.
2951917 [cited on page 194]
Claudio Sacerdoti Coen. 2004. Mathematical knowledge management and interactive theorem
proving. Ph.D. Dissertation. University of Bologna, 2004. Technical Report UBLCS 20045. [cited on page 195]
Marco Comini, Ferruccio Damiani, and Samuel Vrech. 2008. On polymorphic recursion,
type systems, and abstract interpretation. In International Static Analysis Symposium.
Springer, 144–158. [cited on page 194]
Mario Coppo, Mariangiola Dezani-Ciancaglini, and Patrick Sallé. 1979. Functional characterization of some semantic equalities inside λ-calculus. In International Colloquium on
Automata, Languages, and Programming. Springer, 133–146. [cited on page 198]
Thierry Coquand. 1996. An algorithm for type-checking dependent types. Science of Computer Programming 26, 1-3 (1996), 167–177. [cited on page 24]
Haskell Brooks Curry, Robert Feys, William Craig, J Roger Hindley, and Jonathan P Seldin.
1958. Combinatory logic. Vol. 1. North-Holland Amsterdam. [cited on page 65]
Luis Damas and Robin Milner. 1982. Principal Type-Schemes for Functional Programs.
In Proceedings of the 9th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (Albuquerque, New Mexico) (POPL ’82). Association for Computing Machinery, New York, NY, USA, 207–212. https://doi.org/10.1145/582153.582176[cited on pages 3, 4, 15, 18, 145, 148, and 193]

205

Bibliography
Ferruccio Damiani. 2003. Rank 2 intersection types for local definitions and conditional
expressions. ACM Transactions on Programming Languages and Systems (TOPLAS) 25, 4(2003), 401–451. [cited on page 194]
Rowan Davies and Frank Pfenning. 2000. Intersection Types and Computational Effects. In
Proceedings of the Fifth ACM SIGPLAN International Conference on Functional Programming (ICFP ’00). Association for Computing Machinery, New York, NY, USA, 198–208.
https://doi.org/10.1145/351240.351259 [cited on pages 24 and 99]
Leonardo de Moura, Soonho Kong, Jeremy Avigad, Floris Van Doorn, and Jakob von
Raumer. 2015. The Lean theorem prover. (2015). [cited on page 199]
Dominique Devriese, Marco Patrignani, and Frank Piessens. 2017. Parametricity versus theuniversal type. Proceedings of the ACM on Programming Languages 2, POPL (2017), 38.
[cited on page 192]
Joshua Dunfield. 2009. Greedy Bidirectional Polymorphism. In Workshop on ML. [cited on
page 190]
Joshua Dunfield. 2014. Elaborating intersection and union types. Journal of Functional Programming (JFP) 24, 2-3 (2014), 133–165. [cited on page 198]
Jana Dunfield and Neel Krishnaswami. 2020. Bidirectional Typing. arXiv:1908.05839 [cs.PL][cited on page 190]
Joshua Dunfield and Neelakantan R. Krishnaswami. 2013. Complete and Easy Bidirectional
Typechecking for Higher-Rank Polymorphism. In Proceedings of the 18th ACM SIGPLAN
International Conference on Functional Programming (Boston, Massachusetts, USA) (ICFP’13). Association for Computing Machinery, New York, NY, USA, 429–442. https://doi.org/10.1145/2500365.2500582 [cited on pages 3, 6, 7, 12, 23, 25, 28, 30, 61, 81,
85, 95, 109, 114, 121, 125, 126, 130, 131, 132, 134, 154, 161, 182, 189, 190, and 195]
Joshua Dunfield and Neelakantan R. Krishnaswami. 2019.

Sound and Complete Bidi-

rectional Typechecking for Higher-Rank Polymorphism with Existentials and Indexed
Types. Proc. ACM Program. Lang. 3, POPL, Article 9 (Jan. 2019), 28 pages.

https://doi.org/10.1145/3290322 [cited on page 189]
Joshua Dunfield and Frank Pfenning. 2004. Tridirectional Typechecking. SIGPLAN Not.
39, 1 (Jan. 2004), 281–292. https://doi.org/10.1145/982962.964025 [cited on
pages 24, 34, 35, 36, and 190]

206

Bibliography
Richard A Eisenberg. 2016. Dependent types in haskell: Theory and practice. Ph.D. Dissertation. University of Pennsylvania. [cited on pages 185, 194, 195, and 231]
Richard A. Eisenberg. 2018.

Standalone kind signatures.

https://

github.com/ghc-proposals/ghc-proposals/blob/master/proposals/
0054-kind-signatures.rst [cited on page 150]
Richard A. Eisenberg, Dimitrios Vytiniotis, Simon Peyton Jones, and Stephanie Weirich.
2014. Closed type families with overlapping equations. In Proceedings of the 41st ACM
SIGPLAN-SIGACT Symposium on Principles of Programming Languages (San Diego, California, USA) (POPL ’14). ACM, New York, NY, USA, 671–683. https://doi.org/10.
1145/2535838.2535856 [cited on page 184]
Richard A Eisenberg, Stephanie Weirich, and Hamidhasan G Ahmed. 2016. Visible typeapplication. In European Symposium on Programming. Springer, 229–254.

[cited on

page 150]
Joseph Eremondi, Éric Tanter, and Ronald Garcia. 2019. Approximate Normalization for
Gradual Dependent Types. Proc. ACM Program. Lang. 3, ICFP, Article 88 (July 2019),
30 pages. https://doi.org/10.1145/3341692 [cited on page 191]
Phil Freeman. 2017.

PureScript by Example.

Leanpub.

https://leanpub.com/

purescript. [cited on page 199]
Ronald Garcia and Matteo Cimini. 2015. Principal Type Schemes for Gradual Programs. In
Proceedings of the 42nd Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (Mumbai, India) (POPL ’15). Association for Computing Machinery, New York, NY, USA, 303–315. https://doi.org/10.1145/2676726.2676992[cited on pages 12, 27, 85, 91, 103, 104, 109, 162, and 193]
Ronald Garcia, Alison M Clark, and Éric Tanter. 2016. Abstracting Gradual Typing. In
Proceedings of the 43rd Symposium on Principles of Programming Languages. [cited on
pages 61, 62, 68, 74, 75, and 191]
Andrew Gill, John Launchbury, and Simon L Peyton Jones. 1993. A short cut to deforestation. In Proceedings of the conference on Functional programming languages and computerarchitecture. 223–232. [cited on page 5]
Jean-Yves Girard. 1972.

Interprétation fonctionnelle et élimination des coupures del’arithmétique d’ordre supérieur. Ph.D. Dissertation. Éditeur inconnu. [cited on page 170]

207

Bibliography
Jean-Yves Girard. 1986. The System F of Variable Types, Fifteen Years Later. Theoreticalcomputer science 45 (1986), 159–192. [cited on pages 5, 23, and 51]
Warren D Goldfarb. 1981. The undecidability of the second-order unification problem. Theoretical Computer Science 13, 2 (1981), 225–230. [cited on pages 149 and 194]
Roberta Gori and Giorgio Levi. 2002. An experiment in type inference and verification by
abstract interpretation. In International Workshop on Verification, Model Checking, andAbstract Interpretation. Springer, 225–239. [cited on page 194]
Roberta Gori and Giorgio Levi. 2003. Properties of a type abstract interpreter. In International Workshop on Verification, Model Checking, and Abstract Interpretation. Springer,
132–145. [cited on page 194]
Jessica Gronski, Kenneth Knowles, Aaron Tomb, Stephen N Freund, and Cormac Flanagan.
2006. Sage: Hybrid Checking for Flexible Specifications. In Scheme and Functional Programming Workshop. [cited on page 192]
Adam Gundry and Conor McBride. 2013. A tutorial implementation of dynamic pattern
unification. Unpublished draft (2013). [cited on pages 194 and 195]
Adam Gundry, Conor McBride, and James McKinna. 2010. Type inference in context. In
Proceedings of the third ACM SIGPLAN workshop on Mathematically structured functional
programming. ACM, 43–54. [cited on pages 12, 119, 120, 121, 122, 154, and 195]
Adam Michael Gundry. 2013. Type inference, Haskell and dependent types. Ph.D. Dissertation. University of Strathclyde. [cited on pages 149, 185, and 194]
Fergus Henderson, Thomas Conway, Zoltan Somogyi, David Jeffery, Peter Schachte, Simon
Taylor, and Chris Speirs. 1996. The Mercury Language Reference Manual. Technical
Report. [cited on page 199]
Fritz Henglein. 1993. Type inference with polymorphic recursion. ACM Trans. Program.
Lang. Syst. 15, 2 (April 1993), 253–289. https://doi.org/10.1145/169701.169692[cited on pages 150 and 236]
J. Roger Hindley. 1969. The Principal Type-Scheme of an Object in Combinatory Logic.
Trans. Amer. Math. Soc. 146 (1969), 29–60. [cited on pages 3, 4, 15, and 145]
G. Huet. 1973. A unification algorithm for typed lambda calculus. Theoretical Computer
Science 1, 1 (1973), 27–57. [cited on page 149]

208

Bibliography
Yuu Igarashi, Taro Sekiyama, and Atsushi Igarashi. 2017. On Polymorphic Gradual Typing.
In Proceedings of the 22nd International Conference on Functional Programming. [cited on
pages 68, 70, 71, 75, 103, 107, and 192]
Khurram A. Jafery and Joshua Dunfield. 2017. Sums of Uncertainty: Refinements Go
Gradual. In Proceedings of the 44th Symposium on Principles of Programming Languages.
14 pages. [cited on pages 61, 75, and 191]
Trevor Jim. 1996. What are principal typings and what are they good for?. In Proceedingsof the 23rd ACM SIGPLAN-SIGACT symposium on Principles of programming languages.
ACM, 42–53. [cited on page 194]
Mark P Jones. 1995. A system of constructor classes: overloading and implicit higher-order
polymorphism. Journal of functional programming 5, 1 (1995), 1–35. [cited on pages 146,
149, 183, and 195]
Mark P. Jones. 1996. Using Parameterized Signatures to Express Modular Structure. In
Proceedings of the 23rd ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (St. Petersburg Beach, Florida, USA) (POPL ’96). 68–78.

https://doi.org/10.1145/237721.237731 [cited on page 5]Mark P. Jones. 1999. Typing Haskell in Haskell. In Proceedings of the 1999 Haskell Workshop(Haskell ’99), Erik Meijer (Ed.). Paris, France, pp. 9–22. University of Utrecht Technical
Report UU-CS-1999-28. [cited on pages 150, 164, and 194]
Mark P Jones. 2000. Type classes with functional dependencies. In European Symposium on
Programming. Springer, 230–244. [cited on page 67]
Assaf J Kfoury and Jerzy Tiuryn. 1992. Type reconstruction in finite rank fragments of thesecond-order λ-calculus. Information and computation 98, 2 (1992), 228–257. [cited on
page 19]
Oleg Kiselyov. 2013. How OCaml type checker works.

http://okmij.org/ftp/ML/

generalization.html#levels [cited on pages 143 and 144]
Oleg Kiselyov, Ralf Lämmel, and Keean Schupke. 2004. Strongly typed heterogeneous collections. In Proceedings of the 2004 ACM SIGPLAN workshop on Haskell. ACM, 96–107.
[cited on page 67]
Csongor Kiss, Susan Eisenbach, Tony Field, and Simon Peyton Jones. 2019. Higher-ordertype-level programming in Haskell. In Proceedings of the 24th ACM SIGPLAN International Conference on Functional Programming (ICFP 2019). ACM. [cited on page 183]

209

Bibliography
Pieter Koopman, Rinus Plasmeijer, and Jan Martin Jansen. 2014. Church Encoding of Data
Types Considered Harmful for Implementations: Functional Pearl. In Proceedings of the
26nd 2014 International Symposium on Implementation and Application of Functional Languages (Boston, MA, USA) (IFL ’14). Association for Computing Machinery, New York,NY, USA, Article 4, 12 pages. https://doi.org/10.1145/2746325.2746330 [cited
on page 65]
Didier Le Botlan and Didier Rémy. 2003. MLF: Raising ML to the Power of System F (ICFP’03). 12 pages. [cited on pages 6 and 189]
Didier Le Botlan and Didier Rémy. 2009. Recasting MLF. Information and Computation 207,
6 (2009), 726–785. [cited on page 189]
Jukka Lehtosalo et al. 2006. Mypy.

http://www.mypy-lang.org/ [cited on pages 8

and 61]
Daan Leijen. 2009. Flexible Types: Robust Type Inference for First-class Polymorphism(POPL ’09). 12 pages. [cited on pages 6 and 189]
Andres Löh, Conor McBride, and Wouter Swierstra. 2010. A tutorial implementation of adependently typed lambda calculus. Fundamenta informaticae 102, 2 (2010), 177–207.
[cited on page 24]
Simon Marlow et al. 2010. Haskell 2010 language report. Available on: https://www. haskell.
org/onlinereport/haskell2010 (2010). [cited on page 151]
Jacob Matthews and Amal Ahmed. 2008. Parametric polymorphism through run-time sealing or, theorems for low, low prices!. In European Symposium on Programming. Springer,
16–31. [cited on page 80]
Conor McBride. 2002. Faking it Simulating dependent types in Haskell. Journal of functional
programming 12, 4-5 (2002), 375–392. [cited on page 67]
McCracken. 1984. The typechecking of programs with implicit type structure. In Lecture
Notes in Computer Science (Semantics of Data Types, Vol. 173). [cited on page 5]
Dale Miller. 1991. Unification of simply typed lambda-terms as logic programming. (1991).
[cited on page 194]
Robin Milner. 1978. A theory of type polymorphism in programming. Journal of computerand system sciences 17, 3 (1978), 348–375. [cited on page 15]

210

Bibliography
James H. Morris, Jr. 1973. Types Are Not Sets. In Proceedings of the 1st Annual ACM
SIGACT-SIGPLAN Symposium on Principles of Programming Languages (Boston, Massachusetts) (POPL ’73). ACM, New York, NY, USA, 120–124. https://doi.org/10.
1145/512927.512938 [cited on page 192]
James Hiram Morris Jr. 1969. Lambda-calculus models of programming languages. Ph.D.
Dissertation. Massachusetts Institute of Technology. [cited on page 82]
Alan Mycroft. 1984. Polymorphic type schemes and recursive definitions. In International
Symposium on Programming. Springer, 217–228. [cited on page 194]
Georg Neis, Derek Dreyer, and Andreas Rossberg. 2009.

Non-parametric Parametric-

ity. In Proceedings of the 14th ACM SIGPLAN International Conference on Functional
Programming (Edinburgh, Scotland) (ICFP ’09). ACM, New York, NY, USA, 135–148.
https://doi.org/10.1145/1596550.1596572 [cited on page 80]
Max S. New, Dustin Jamner, and Amal Ahmed. 2019. Graduality and Parametricity: Together
Again for the First Time. Proc. ACM Program. Lang. 4, POPL, Article 46 (Dec. 2019),
32 pages. https://doi.org/10.1145/3371114 [cited on page 192]
Martin Odersky and Konstantin Läufer. 1996. Putting Type Annotations to Work. In Proceedings of the 23rd ACM SIGPLAN-SIGACT Symposium on Principles of Programming
Languages (St. Petersburg Beach, Florida, USA) (POPL ’96). Association for Computing Machinery, New York, NY, USA, 54–67.

https://doi.org/10.1145/237721.

237729 [cited on pages 3, 7, 15, 18, 19, 20, 68, 132, 145, and 189]
Martin Odersky, Christoph Zenger, and Matthias Zenger. 2001. Colored Local Type Inference (POPL ’01). 13 pages. [cited on pages 37 and 190]
Bruno C. d. S. Oliveira, Zhiyuan Shi, and João Alpuim. 2016. Disjoint intersection types. In
International Conference on Functional Programming (ICFP). [cited on page 198]
Michel Parigot. 1992. Recursive programming with proofs. Theoretical Computer Science 94,
2 (1992), 335–356. [cited on page 65]
Simon Peyton Jones. 2003. Haskell 98 language and libraries: the revised report. Cambridge
University Press. [cited on page 147]
Simon Peyton Jones. 2020.

Simplify subsumption.

https://github.

com/ghc-proposals/ghc-proposals/blob/master/proposals/
0287-simplify-subsumption.rst [cited on pages 23 and 189]

211

Bibliography
Simon Peyton Jones, Mark Jones, and Erik Meijer. 1997. Type classes: exploring the design
space. In Haskell workshop, Vol. 1997. [cited on page 67]
Simon Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, and Mark Shields. 2007. Practical type inference for arbitrary-rank types. Journal of functional programming 17, 1(2007), 1–82.

[cited on pages 3, 6, 7, 15, 18, 24, 28, 29, 35, 38, 39, 45, 46, 47, 53, 81,

134, 145, 149, 182, 183, 189, and 193]
Simon Peyton Jones, Dimitrios Vytiniotis, Stephanie Weirich, and Geoffrey Washburn. 2006.
Simple unification-based type inference for GADTs. In Proceedings of the Eleventh ACM
SIGPLAN International Conference on Functional Programming (Portland, Oregon, USA)(ICFP ’06). ACM, New York, NY, USA, 50–61. https://doi.org/10.1145/1159803.
1159811 [cited on pages 145 and 183]
Benjamin C Pierce. 2002. Types and programming languages. [cited on page 81]
Benjamin C. Pierce and David N. Turner. 2000. Local Type Inference. ACM Trans. Program. Lang. Syst. 22, 1 (Jan. 2000), 1–44. https://doi.org/10.1145/345099.345100[cited on pages 7, 11, 23, 56, and 190]
François Pottier and Didier Rémy. 2005. The essence of ML type inference. Advanced Topics
in Types and Programming Languages (2005). [cited on pages 193, 194, and 231]
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 page 198]
Jason Reed. 2009. Higher-order constraint simplification in dependent type theory. In Proceedings of the Fourth International Workshop on Logical Frameworks and Meta-Languages:
Theory and Practice. ACM, 49–56. [cited on page 194]
Didier Rémy. 1992. Extension of ml type system with a sorted equational theory on types.
(1992). [cited on pages 143 and 144]
Didier Rémy. 2005. Simple, Partial Type-inference for System F Based on Type-containment(ICFP ’05). 14 pages. [cited on page 190]
Didier Rémy and Boris Yakobowski. 2008. From ML to MLF: Graphic Type Constraints with
Efficient Type Inference (ICFP ’08). 12 pages. [cited on page 189]
John C Reynolds. 1974. Towards a theory of type structure. In Programming Symposium.
Springer, 408–425. [cited on page 5]212

Bibliography
John C. Reynolds. 1983. Types, Abstraction and Parametric Polymorphism. In Proceedingsof the IFIP 9th World Computer Congress. [cited on page 80]
John C. Reynolds. 1991. The Coherence of Languages with Intersection Types. In Proceedingsof the International Conference on Theoretical Aspects of Computer Software.

[cited on

page 82]
Patrick Salle. 1978. Une Extension De La Theorie Des Types En lambda-Calcul. In Proceedings of the Fifth Colloquium on Automata, Languages and Programming. Springer-Verlag,
London, UK, UK, 398–410. [cited on page 198]
Tom Schrijvers, Simon Peyton Jones, Martin Sulzmann, and Dimitrios Vytiniotis. 2009.
Complete and decidable type inference for GADTs. In Proceedings of the 14th ACM SIGPLAN International Conference on Functional Programming (Edinburgh, Scotland) (ICFP’09). ACM, New York, NY, USA, 341–352.

https://doi.org/10.1145/1596550.

1596599 [cited on page 193]
Alejandro Serrano, Jurriaan Hage, Simon Peyton Jones, and Dimitrios Vytiniotis. 2020. A
Quick Look at Impredicativity. Proc. ACM Program. Lang. 4, ICFP, Article 89 (Aug. 2020),
29 pages. https://doi.org/10.1145/3408971 [cited on pages 6, 23, and 190]
Alejandro Serrano, Jurriaan Hage, Dimitrios Vytiniotis, and Simon Peyton Jones. 2018.
Guarded impredicative polymorphism. In Proceedings of the 39th ACM SIGPLAN Conference on Programming Language Design and Implementation (Philadelphia, PA, USA)
(PLDI 2018). ACM, New York, NY, USA, 783–796.

https://doi.org/10.1145/

3192366.3192389 [cited on pages 6, 23, and 190]
Paula Severi and Erik Poll. 1994. Pure Type Systems with Definitions. Logical Foundationsof Computer Science (1994), 316–328. [cited on page 197]
Jeremy Siek, Ronald Garcia, and Walid Taha. 2009. Exploring the design space of higherorder casts. In European Symposium on Programming. 17–31. [cited on page 191]
Jeremy G. Siek and Walid Taha. 2006. Gradual Typing for Functional Languages. In Proceedings of the 2006 Scheme and Functional Programming Workshop. [cited on pages 8, 11, 61,
101, and 191]
Jeremy G. Siek and Walid Taha. 2007. Gradual Typing for Objects. In European Conference
on Object-Oriented Programming. [cited on pages 6, 9, 11, 61, 62, 63, 64, 68, 71, 73, 98,
99, 101, and 191]

213

Bibliography
Jeremy G. Siek and Manish Vachharajani. 2008. Gradual Typing with Unification-based Inference. In Proceedings of the 2008 Symposium on Dynamic Languages. [cited on page 193]
Jeremy G Siek, Michael M Vitousek, Matteo Cimini, and John Tang Boyland. 2015. Refined
criteria for gradual typing. In 1st Summit on Advances in Programming Languages (SNAPL2015). Schloss Dagstuhl-Leibniz-Zentrum fuer Informatik. [cited on pages 9, 11, 12, 75,
78, 83, 85, and 191]
Vincent Simonet and François Pottier. 2007. A constraint-based approach to guarded algebraic data types. ACM Transactions on Programming Languages and Systems (TOPLAS)
29, 1 (2007), 1. [cited on page 193]
Matthieu Sozeau and Nicolas Oury. 2008. First-Class Type Classes. In TPHOLs ’08 (Montreal,
P.Q., Canada). Springer-Verlag, 278–293. [cited on page 199]
Sam Tobin-Hochstadt. 2019. Gradual Typing from Theory to Practice.

https://blog.

sigplan.org/2019/07/12/gradual-typing-theory-practice/ [cited on page 8]
Matías Toro, Elizabeth Labrada, and Éric Tanter. 2019. Gradual Parametricity, Revisited.
Proc. ACM Program. Lang. 3, POPL, Article 17 (Jan. 2019), 30 pages. https://doi.
org/10.1145/3290330 [cited on page 192]
Julien Verlaguet. 2013. Facebook: Analyzing PHP statically. In Proceedings of Commercial
Users of Functional Programming. [cited on page 61]
Michael M. Vitousek, Andrew M. Kent, Jeremy G. Siek, and Jim Baker. 2014. Design and
Evaluation of Gradual Typing for Python. In Proceedings of the 10th Symposium on Dynamic languages. [cited on pages 8 and 61]
Dimitrios Vytiniotis, Simon Peyton Jones, Tom Schrijvers, and Martin Sulzmann. 2011. OutsideIn (X) Modular type inference with local assumptions. Journal of functional programming 21, 4-5 (2011), 333–412. [cited on pages 149, 182, 183, 185, 193, 200, 231, and 232]
Dimitrios Vytiniotis, Stephanie Weirich, and Simon Peyton Jones. 2006. Boxy types: inference for higher-rank types and impredicativity. In Proceedings of the Eleventh ACM
SIGPLAN International Conference on Functional Programming (Portland, Oregon, USA)(ICFP ’06). ACM, New York, NY, USA.

https://doi.org/10.1145/1159803.

1159811 [cited on page 37]
Dimitrios Vytiniotis, Stephanie Weirich, and Simon Peyton Jones. 2008. FPH: First-classPolymorphism for Haskell (ICFP ’08). 12 pages. [cited on page 190]

214

Bibliography
P. Wadler and S. Blott. 1989. How to Make Ad-hoc Polymorphism Less Ad Hoc. In POPL
’89. ACM. [cited on page 199]
Philip Wadler and Robert Bruce Findler. 2009. Well-Typed Programs Can’t Be Blamed.
In Proceedings of the 18th European Symposium on Programming Languages and Systems.
[cited on page 192]
Stephanie Weirich, Pritam Choudhury, Antoine Voizard, and Richard A. Eisenberg. 2019. A
Role for dependent types in Haskell. Proc. ACM Program. Lang. 3, ICFP, Article 101 (July
2019), 29 pages. https://doi.org/10.1145/3341705 [cited on page 194]
Stephanie Weirich, Justin Hsu, and Richard A. Eisenberg. 2013. System FC with Explicit Kind
Equality. In Proceedings of the 18th ACM SIGPLAN International Conference on Functional Programming (Boston, Massachusetts, USA) (ICFP ’13). ACM, New York, NY, USA,
275–286. https://doi.org/10.1145/2500365.2500599 [cited on pages 146, 148,
and 194]
Stephanie Weirich, Antoine Voizard, Pedro Henrique Azevedo de Amorim, and Richard A
Eisenberg. 2017. A specification for dependent types in Haskell. In Proceedings of the 22th
ACM SIGPLAN International Conference on Functional Programming (ICFP ’17). ACM.
[cited on pages 170 and 194]
Joe B Wells. 1999. Typability and Type Checking in System F are Equivalent and Undecidable.
Annals of Pure and Applied Logic 98, 1-3 (1999), 111–156. [cited on pages 6, 7, 19, and 91]
Thomas Winant, Dominique Devriese, Frank Piessens, and Tom Schrijvers. 2014. Partial
type signatures for haskell. In International Symposium on Practical Aspects of Declarative
Languages. Springer, 17–32. [cited on page 230]
A.K. Wright and M. Felleisen. 1994. A Syntactic Approach to Type Soundness. Inf. Comput.
115, 1 (Nov. 1994), 38–94. https://doi.org/10.1006/inco.1994.1093 [cited on
page 51]
Hongwei Xi, Chiyan Chen, and Gang Chen. 2003. Guarded recursive datatype constructors.
In Proceedings of the 30th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (New Orleans, Louisiana, USA) (POPL ’03). ACM, New York, NY, USA,
224–235. https://doi.org/10.1145/604131.604150 [cited on page 183]
Hongwei Xi and Frank Pfenning. 1999. Dependent Types in Practical Programming. In
Proceedings of the 26th ACM SIGPLAN-SIGACT Symposium on Principles of Programming

215

Bibliography
Languages (San Antonio, Texas, USA) (POPL ’99). Association for Computing Machinery,
New York, NY, USA, 214–227. https://doi.org/10.1145/292540.292560 [cited
on pages 24 and 197]
Ningning Xie, Xuan Bi, and Bruno C d S Oliveira. 2018. Consistent Subtyping for All. In
European Symposium on Programming. Springer, 3–30. [cited on pages 13 and 27]
Ningning Xie, Xuan Bi, Bruno C. D. S. Oliveira, and Tom Schrijvers. 2019a. Consistent
Subtyping for All. ACM Transactions on Programming Languages and Systems 42, 1, Article
2 (Nov. 2019), 79 pages. https://doi.org/10.1145/3310339 [cited on pages 13
and 27]
Ningning Xie, Bruno C. d. S. Oliveira, Xuan Bi, and Tom Schrijvers. 2020.

Row and

Bounded Polymorphism via Disjoint Polymorphism. In 34th European Conference on
Object-Oriented Programming (ECOOP 2020) (Leibniz International Proceedings in Informatics (LIPIcs), Vol. 166), Robert Hirschfeld and Tobias Pape (Eds.). Schloss Dagstuhl–
Leibniz-Zentrum für Informatik, Dagstuhl, Germany, 27:1–27:30. https://doi.org/
10.4230/LIPIcs.ECOOP.2020.27 [cited on page 198]
Ningnign Xie and Richard A Eisenberg. 2018. Coercion Quantification. In Haskell Implementors’Workshop. [cited on page 194]
Ningning Xie, Richard A Eisenberg, and Bruno CDS Oliveira. 2019b. Kind Inference for
Datatypes: Technical Supplement. arXiv preprint arXiv:1911.06153 (2019). https://
arxiv.org/abs/1911.06153 [cited on pages 13 and 194]
Ningning Xie, Richard A. Eisenberg, and Bruno C. d. S. Oliveira. 2019c. Kind Inference for
Datatypes. Proc. ACM Program. Lang. 4, POPL, Article 53 (Dec. 2019), 28 pages. https://doi.org/10.1145/3371121 [cited on page 13]
Ningning Xie and Bruno C d S Oliveira. 2017. Towards Unification for Dependent Types. In
Draft Proceedings of the 18th Symposium on Trends in Functional Programming (TFP ’18).
Extended abstract. [cited on pages 13 and 140]
Ningning Xie and Bruno C d S Oliveira. 2018. Let Arguments Go First. In European Symposium on Programming. Springer, 272–299. [cited on pages 13, 38, and 57]
Brent A. Yorgey, Stephanie Weirich, Julien Cretin, Simon Peyton Jones, Dimitrios Vytiniotis,
and José Pedro Magalhães. 2012. Giving Haskell a Promotion. In Proceedings of the 8th
ACM SIGPLAN Workshop on Types in Language Design and Implementation (Philadelphia,216

Bibliography
Pennsylvania, USA) (TLDI ’12). ACM, New York, NY, USA, 53–66. https://doi.org/
10.1145/2103786.2103795 [cited on pages 146, 148, and 230]
Beta Ziliani and Matthieu Sozeau. 2015. A Unification Algorithm for Coq Featuring Universe
Polymorphism and Overloading. In Proceedings of the 20th ACM SIGPLAN International
Conference on Functional Programming (Vancouver, BC, Canada) (ICFP 2015). ACM, New
York, NY, USA, 179–191. https://doi.org/10.1145/2784731.2784751 [cited on
page 194]217

Part VI
Technical Appendix

219

A Full Rules for Algorithmic AP(S1 , N1 ) `AP σ <: σ2 ,→ (S2 , N2 )(Algorithmic Subtyping)ap-a-s-mono

S0 `AP τ1 ≈ τ2 ,→ S1(S0 , N0 ) `AP τ1 <: τ2 ,→ (S1 , N0 )ap-a-s-arrowL(S0 , N0 ) `AP σ . σ3 → σ4 ,→ (S1 , N1 )(S1 , N1 ) `AP σ3 <: σ1 ,→ (S2 , N2 )(S2 , N2 ) `AP σ2 <: σ4 ,→ (S3 , N3 )(S0 , N0 ) `AP σ1 → σ2 <: σ ,→ (S3 , N3 )ap-a-s-arrowR(S0 , N0 ) `AP σ . σ1 → σ2 ,→ (S1 , N1 )(S1 , N1 ) `AP σ3 <: σ1 ,→ (S2 , N2 )(S2 , N2 ) `AP σ2 <: σ4 ,→ (S3 , N3 )(S0 , N0 ) `AP σ <: σ3 → σ4 ,→ (S3 , N3 )ap-a-s-forallLb <: σ2 ,→ (S1 , N1 )(S0 , N0 ) `AP σ1 [a 7→ β]b `AP ∀a. σ1 <: σ2 ,→ (S1 , N1 )(S0 , N0 β)

ap-a-s-forallR(S0 , N0 ) `AP σ1 <: σ2 [a 7→ b] ,→ (S1 , N1 )b ∈/ fv (S(σ1 ))b ∈/ fv (S(∀a. σ2 ))(S0 , N0 b) `AP σ1 <: ∀a. σ2 ,→ (S1 , N1 )(S1 , N1 ); Σ `AP σ <: σ2 ,→ (S2 , N2 )(Algorithmic Application Subtyping)ap-a-as-empty(S0 , N0 ); • `AP σ <: σ ,→ (S0 , N0 )ap-a-as-forallb <: σ2 ,→ (S1 , N1 )(S0 , N0 ); Σ, σ3 `AP σ1 [a 7→ β]b Σ, σ3 `AP ∀a. σ1 <: σ2 ,→ (S1 , N1 )(S0 , N0 β);

221A Full Rules for Algorithmic AP
ap-a-as-arrow(S0 , N0 ) `AP σ3 <: σ1 ,→ (S1 , N1 )(S1 , N1 ); Σ `AP σ2 <: σ4 ,→ (S2 , N2 )(S0 , N0 ); Σ, σ3 `AP σ1 → σ2 <: σ3 → σ4 ,→ (S2 , N2 )ap-a-as-mono(S0 , N0 ) `AP τ . τ1 → τ2 ,→ (S1 , N1 )(S1 , N1 ); Σ, σ3 `AP τ1 → τ2 <: σ ,→ (S2 , N2 )b Σ, σ3 `AP τ <: σ ,→ (S2 , N2 )(S0 , N0 β);(S1 , N1 ) `AP σ . σ1 → σ2 ,→ (S2 , N2 )(Matching)ap-a-m-tvar

S0 `AP αb≈αb1 → αb2 ,→ S1
(S0 , N0 αb1 αb2 ) `AP αb.αb1 → αb2 ,→ (S1 , N0 )ap-a-m-arrow(S0 , N0 ) `AP σ1 → σ2 . σ1 → σ2 ,→ (S0 , N0 )S1 `AP τ1 ≈ τ2 ,→ S2

ap-a-u-refl

S0 `AP τ ≈ τ ,→ S0
ap-a-u-evarL

α
b∈
/ S0

(Unification)
ap-a-u-solvedEvarLαb ∈ S0S0 `AP S0 (bα) ≈ τ ,→ S1
S0 `AP αb ≈ τ ,→ S1

ap-a-u-solvedEvarR

αb ∈/ fv (S0 (τ ))αb ∈ S0S0 `AP αb ≈ τ ,→ [bα 7→ S0 (τ )] · S1
ap-a-u-evarR

α
b∈
/ S0

S0 `AP τ ≈ α
b ,→ S1
ap-a-u-arrowαb ∈/ fv (S0 (τ ))S0 `AP τ ≈ α
b ,→ [bα 7→ S0 (τ )] · S1

222

S0 `AP τ ≈ S0 (bα) ,→ S1S0 `AP τ1 ≈ τ3 ,→ S1

S1 `AP τ2 ≈ τ4 ,→ S2

S0 `AP τ1 → τ2 ≈ τ3 → τ4 ,→ S2

(S1 , N1 ); Ψ `AP e ⇒ σ ,→ (S2 , N2 )(Algorithmic Typing Inference)

ap-a-inf-int(S0 , N0 ); Ψ `AP n ⇒ Int ,→ (S0 , N0 )ap-a-inf-lam(S0 , N0 ); Ψ, x : βb `AP e ⇒ σ ,→ (S1 , N1 )b Ψ `AP λx. e ⇒ βb → σ ,→ (S1 , N1 )(S0 , N0 β);
ap-a-inf-lamann(S0 , N0 ); Ψ, x : σ1 `AP e ⇒ σ2 ,→ (S1 , N1 )(S0 , N0 ); Ψ `AP λx : σ1 . e ⇒ σ1 → σ2 ,→ (S1 , N1 )(S1 , N1 ); Ψ; Σ `AP e ⇒ σ ,→ (S2 , N2 )(Algorithmic Typing Application Mode)ap-a-app-var(x : σ1 ) ∈ Ψ(S0 , N0 ); Σ `AP σ1 <: σ2 ,→ (S1 , N1 )(S0 , N0 ); Ψ; Σ `AP x ⇒ σ2 ,→ (S1 , N1 )ap-a-app-lam(S0 , N0 ); Ψ, x : σ1 `AP e ⇒ σ2 ,→ (S1 , N1 )(S0 , N0 ); Ψ; Σ, σ1 `AP λx. e ⇒ σ1 → σ2 ,→ (S1 , N1 )ap-a-app-lamann(S0 , N0 ) `AP σ2 <: σ1 ,→ (S1 , N1 )(S1 , N1 ); Ψ, x : σ1 `AP e ⇒ σ3 ,→ (S2 , N2 )(S0 , N0 ); Ψ; Σ, σ2 `AP λx : σ1 . e ⇒ σ2 → σ3 ,→ (S2 , N2 )ap-a-app-app(S0 , N0 ); Ψ `AP e2 ⇒ σ1 ,→ (S1 , N1 ai i )ibi 7→ ai ]σ2 = ∀ai i . σ1 [ αiαbi = fv (S1 (σ1 )) − fv (S1 (Ψ))(S1 , N1 ); Ψ; Σ, σ2 `AP e1 ⇒ σ2 → σ3 ,→ (S2 , N2 )(S0 , N0 ); Ψ; Σ `AP e1 e2 ⇒ σ3 ,→ (S2 , N2 )223

B The Extended Algorithmic GPC
B.1 Syntax
Expressionse::=

x | n | λx : σ. e | λx. e | e1 e2 | e : σ | let x = e1 in e2

Types

σ

::=

Int | a | α
b | σ1 → σ2 | ∀a. σ | ? | S | G

Monotypes

τ::=Int | a | α
b | τ1 → τ2 | S | G

Existential variables

α
b

::=

bS | αbG
αCastable Types

C::=

Int | a | α
b | C1 → C2 | ∀a. C | ? | G

Castable Monotypest::=

Int | a | α
b | t1 → t2 | G

Algorithmic Contexts

Γ, ∆, Θ::=• | Γ, x : σ | Γ, a | Γ, αb | Γ, αbS = τ | Γ, αbG = t | Γ, ▶αbΩ::=• | Ω, x : σ | Ω, a | Ω, αbS = τ | Ω, αbG = t | Ω, ▶αbComplete Contexts

B.2 Type SystemΓ `G σ ≲ aB a ∆(Algorithmic Consistent Subtyping)

gpc-as-tvar

gpc-as-evar

gpc-as-int

Γ[a] `G a ≲ a a Γ[a]

Γ[bα ] `G αb≲αb a Γ[bα]Γ `G Int ≲ Int a Γ

gpc-as-arrowgpc-as-forallRΓ ` σ3 ≲ σ1 a ΘGΘ ` [Θ]σ2 ≲ [Θ]σ4 a ∆GΓ, a `G σ1 ≲ σ2 a ∆, a, ΘΓ `G σ 1 → σ 2 ≲ σ 3 → σ 4 a ∆gpc-as-forallLLΓ, ▶baS , αbS `G σ1 [a 7→ αbS ] ≲ σ2 a ∆, ▶baS , ΘΓ `G ∀a. σ1 ≲ σ2 a ∆Γ `G σ1 ≲ ∀a. σ2 a ∆gpc-as-sparΓ `G S ≲ S a Γ

gpc-as-gpar

gpc-as-unknownLLΓ `G G ≲ G a ΓΓ `G ? ≲ C a contaminate(Γ, C)

225

B The Extended Algorithmic GPC
gpc-as-instL

gpc-as-unknownRRαb∈/ fv(σ)Γ `G C ≲ ? a contaminate(Γ, C)Γ[bα ] `G αb⪅σa∆Γ[bα ] `G αb≲σa∆

gpc-as-instRαb∈/ fv(σ)Γ[bα ] `G σ ⪅ αba∆

Γ[bα ] `G σ ≲ αba∆Γ `G αb⪅σa∆(Instantiation I)

gpc-instl-solveS

gpc-instl-solveGΓ `G τ

Γ `G tΓ, αbS , Γ′ `G αbS ⪅ τ a Γ, αbS = τ, Γ′Γ, αbG , Γ′ `G αbG ⪅ t a Γ, αbG = t, Γ′

gpc-instl-solveUS

gpc-instl-solveUG

Γ[bα S ] `G αbS ⪅ ? a Γ[bαG , αbS = αbG ]Γ[bα G ] `G αbG ⪅ ? a Γ[bαG ]gpc-instl-reachSG1

Γ[bαS ][βbG ] `G αbS ⪅ βbG a Γ[bαG , αbS = αbG ][βbG = αbG ]gpc-instl-reachSG2

Γ[βbS ][bα G ] `G αbG ⪅ βbS a Γ[βbG , βbS = βbG ][bαG = βbG ]gpc-instl-reachOtherb `G αΓ[bα][β]b ⪅ βb a Γ[bα][βb = αb]
gpc-instl-arrΓ[bα2 , αb1 , αb=αb1 → αb 2 ] `G σ 1 ⪅ α
b1 a Θ

Θ `G αb2 ⪅ [Θ]σ2 a ∆Γ[bα ] `G αb ⪅ σ1 → σ2 a ∆gpc-instl-forallRΓ[bα], b `G αb ⪅ σ a ∆, b, Θ
Γ[bα ] `G αb ⪅ ∀b. σ a ∆226

B.2 Type SystemΓ `G σ ⪅ αba∆(Instantiation II)

gpc-instr-solveS

gpc-instr-solveGΓ `G τ

Γ `G tΓ, αbS , Γ′ `G τ ⪅ αbS a Γ, αbS = τ, Γ′Γ, αbG , Γ′ `G t ⪅ αbG a Γ, αbG = t, Γ′

gpc-instr-solveUS

gpc-instr-solveUG

Γ[bα S ] `G ? ⪅ αbS a Γ[bαG , αbS = αbG ]Γ[bα G ] `G ? ⪅ αbG a Γ[bαG ]gpc-instr-reachSG1

Γ[bαS ][βbG ] `G βbG ⪅ αbS a Γ[bαG , αbS = αbG ][βbG = αbG ]gpc-instr-reachSG2

Γ[βbS ][bαG ] `G βbS ⪅ αbG a Γ[βbG , βbS = βbG ][bαG = βbG ]gpc-instr-reachOtherb `G βb ⪅ α
Γ[b
α][β]
b a Γ[bα][βb = αb]
gpc-instr-arrΓ[bα2 , αb1 , αb=αb1 → αb 2 ] `G αb 1 ⪅ σ1 a ΘΘ `G [Θ]σ2 ⪅ αb2 a ∆

Γ[bα ] `G σ 1 → σ 2 ⪅ αba∆
gpc-instr-forallLL

Γ[bα], ▶bbS , βbS `G σ[b 7→ βbS ] ⪅ αb a ∆, ▶bbS , ΘΓ[bα] `G ∀b. σ ⪅ αba∆Γ `G e ⇒ σ a ∆(Inference)
gpc-inf-var(x : σ) ∈ ΓΓ `G x ⇒ σ a Γ

gpc-inf-int

Γ `G n ⇒ Int a Γ

gpc-inf-lamann2Γ `G σΓ, βbS , x : σ `G e ⇐ βbS a ∆, x : σ, ΘΓ `G λx : σ. e ⇒ σ → βbS a ∆gpc-inf-lam2

Γ, αbS , βbS , x : αbS `G e ⇐ βbS a ∆, x : αbS , Θ
Γ `G λx. e ⇒ αbS → βbS a ∆gpc-inf-annoΓ `G σΓ `G e ⇐ σ a ∆Γ `G e : σ ⇒ σ a ∆227

B The Extended Algorithmic GPC
gpc-inf-app

Γ `G e 1 ⇒ σ a Θ 1

Θ1 `G [Θ1 ]σ . σ1 → σ2 a Θ2

Θ2 `G e2 ⇐ [Θ2 ]σ1 a ∆Γ `G e 1 e 2 ⇒ σ 2 a ∆gpc-inf-let2

Γ `G e 1 ⇒ σ a Θ 1

Θ1 , αb S , x : σ `G e 2 ⇐ αbS a ∆, x : σ, Θ2Γ `G let x = e1 in e2 ⇒ αbS a ∆

Γ `G e ⇐ σ a ∆(Checking)gpc-chk-lam

gpc-chk-gen

Γ, x : σ1 ` e ⇐ σ2 a ∆, x : σ1 , Θ

Γ, a `G e ⇐ σ a ∆, a, ΘΓ `G λx. e ⇐ σ1 → σ2 a ∆Γ `G e ⇐ ∀a. σ a ∆G

gpc-chk-subΓ `G e ⇒ σ 1 a ΘΘ `G [Θ]σ1 ≲ [Θ]σ2 a ∆Γ `G e ⇐ σ 2 a ∆Γ `G σ . σ 1 → σ 2 a ∆(Algorithmic Matching)gpc-am-forallLΓ, αbS `G σ[a 7→ αb S ] . σ 1 → σ2 a ∆Γ `G ∀a. σ . σ1 → σ2 a ∆228

gpc-am-arr

Γ `G σ 1 → σ 2 . σ 1 → σ 2 a Γ

gpc-am-unknown

gpc-am-var

Γ `G ? . ? → ? a ΓΓ[bα ] `G αb.α
b1 → αb2 a Γ[bα1 , αb2 , αb=αb1 → αb2 ]C Kind Inference for Datatypes
C.1 Other Language Extensions
This section accompanies Section 7.8 of the main paper, including discussion about more
related language extensions. These extensions affect kind inference, but not in a fundamental
way.

C.1.1 Visible Dependent Quantification
Besides specified type variables for which users can optionally provide type arguments, Haskell
also incorporates visible dependent quantification (VDQ)1 , e.g., type T :: ∀(k :: ?) → k → ?,with which users are forced to provide type arguments to T . That is, one would use T with,e.g., T ? Int and T (? → ?) Maybe, never just T Int. Visible dependent quantification is
Haskell’s equivalent to routine dependent quantification in dependently typed languages.
To support VDQ, rule dt-tt needs to be extended, as VDQ brings variables into scopefor later reference. For example, given
data T :: ∀(k :: ?) → k → ?
data T k a = MkT
We should get a context k :: ?, a :: k when checking MkT .
VDQ opens an interesting design choice: should unannotated type variables be able tointroduce VDQ? For example, in the definition of P below, we use f and a as the arguments
to T . To make it type-check, we need to infer P :: ∀(f :: ?) → f → ?.
data P f a = MkP (T f a)
However, the tricky part with inferring the kind of P is that we cannot have a fixed initial
form of the kind of P, i.e., α
b → βb → ? or ∀(f : αb) → βb → ?, when type-checking therec group of P, until we type-check P’s body. In order to avoid this challenge, we support
GHC’s current ruling on the matter: dependent variables must be manifestly so. That is, the1

VDQ is implemented in GHC 8.10.

229

C Kind Inference for Datatypes
initial kind of a datatype includes VDQ only for those variables that appear, lexically, in thekind of a variable; other type parameters are reflected in a datatype’s initial kind with a regular(non-dependent) arrow. This guideline rejects P as an example of non-manifest dependency.

C.1.2 Datatype Promotion
Haskellers can use datatypes as kinds and can write data constructors in types [Yorgey et al.
2012]. In the PolyKinds system, types and kinds are mixed (allowing datatypes to be used as
kinds), but there is no facility to use a data constructor in a type.
To support such usage, the kinding judgment must now use the term context to fetch thetype of data constructors. Moreover, dependency analysis needs to take dependencies ondata constructors into account.
Definition 24 (Dependency Analysis with Type-Level Data). We extend Definition 20 with(iii) The definition of T1 depends on the definition of T2 if T1 uses data constructorsof T2 .
While the appearance of data constructors in types enriches the type language considerably, they do not pose a particular challenge for inference; the rest of our presentation would
remain unaffected.

C.1.3 Partial Type Signatures
For quite some time, GHC has supported kind signatures on a subset of a datatype’s parameters, much like the partial type signatures described by Winant et al. [2014]. For example,
App, below, does not have a signature but still has a kind annotation for f .
data App (f :: ? → ?) a = A (f a)
To deal with such a construct we first need to amend the syntax of a datatype declaration to
support kind annotations for variables.
datatype decl.

T::=data T φ = DjjKind annotations can also contain free variables, which need to be generalized in a similar
way as signatures. For example, T2 has kind ∀{k :: ?}. ∀(f :: k). ?.
data T2 (f :: k) = MkT2
Supporting these partial signatures adds complication to rule pgm-dt-tt (and its algorithmic counterpart) to bring the kind variables into scope. However, and critically, a partial

230C.2 Today’s GHC
signature will still go via rule pgm-dt-tt, never rule pgm-dt-ttS, used for full signatures
only. This means that a partial type signature does not unlock polymorphic recursion: thedatatype will considered monomorphic and ungeneralized within its own recursive group.

C.2 Today’s GHC
Our Chapter 7 describes, in depth, how kind inference can work for datatype declarations.
Here, we review how our work relates to GHC. To make the claims concrete, this section
contains references to specific stretches of code within GHC.

C.2.1 Constraint-Based Type InferenceType inference in GHC is based on generating and solving constraints [Pottier and Rémy
2005; Vytiniotis et al. 2011], distinct from our approach here, where we unify on the fly.
Despite this different architecture, our results carry over to the constraint-based style. Insteadof using eager unification, we can imagine accumulating constraints in output contexts Θ,and then invoking a solver to extend the context with solutions. This approach is taken by
Eisenberg [2016].
In thinking about the change from eager unification to delayed constraints, one might
worry about information loss around any place where we apply a context as a substitution,
as these substitutions would be empty in a constraint-solving approach without eager unification. At top-level (Figure 7.12), a constraint-solving approach would run the constraint
solver, and the substitutions would contain the same mappings as our approach provides.
Conversely, the relations in Figure 7.15 would become part of the constraint solver, so substituting here is safe, too. A potential problem arises in rule a-ktt-app (Figure 7.14), where
we substitute in the function’s kind before running the kind-directed ⊩kapp judgment. However, our system is predicative: it never unifies a type variable with a polytype. Thus, the substitution in rule a-ktt-app can never trigger a new usage of rule a-kapp-tt-forall. It candistinguish between rule a-kapp-tt-arrow and rule a-kapp-tt-kuvar, but we conjecturethat the choice between these rules is irrelevant: both will lead to equivalent substitutions in
the end.

C.2.2 Contexts
A typing context is not maintained in much of GHC’s inference algorithm. Instead, a variable’s kind is stored in the data structure representing the variable. This is very convenient,
as it means that looking up a variable’s type or kind is a pure, fast operation. One downside231

C Kind Inference for Datatypes
is that the compiler must maintain an extra invariant that all occurrences of a variable store
the same kind; this is straightforward to maintain in practice.
Beyond just storing variables’ kinds, the typing context in this work also critically storesvariables’ ordering. Lacking contexts, GHC uses a different mechanism: level numbers, originally invented to implement untouchability [Vytiniotis et al. 2011, Section 5.1]. Every typevariable in GHC is assigned a level number during inference. Type variables contain a structure that includes level numbers. Roughly, the level number of a type variable a corresponds
to the number of type variables in scope before a. Accordingly, we can tell the relative order(in a hypothetical context, according to the systems in this work) of two variables simply bycomparing their level numbers. One of GHC’s invariants is that a unification variable at leveln is never unified with a type that mentions a variable with a level number m > n; this ismuch like the extra checks in the unification judgments in our work.
The local scopes of this work are also tracked by GHC. All the variables in the same local
scope are assigned the same level number, and they are flagged as reorderable. After inference
is complete, GHC does a topological sort to get the final order.
A final role that contexts play in our formalism is that they store solutions for unification
variables; we apply contexts as a substitution. In GHC, unification variables store mutable
cells that get filled in. It has a process called zonking 2 , which is exactly analogous to ouruse of contexts as substitutions. Zonking a unification variable replaces the variable with its
solution, if any.

C.2.3 Unification
The solver in GHC still has to carry out unification, much along the lines of the unification
judgment we present here. This algorithm has to deal with the heterogeneous unification
problems we consider, as well. Indeed, GHC’s unification algorithm recurs into the kinds of a
unification variable and the type it is unifying with, just as ours does. As implied by our focus
on decidability of unification, there have been a number of bugs in GHC’s implementationthat led to loops in the type checker; the most recent is #16902.
GHC actually uses several unification algorithms internally. It has an eager unifier, much
like the one we describe. When that unifier fails, it generates the constraint that is sent to thesolver. (The eager unifier is meant solely to be an optimization.) There is also a unifier meant
to work after type inference is complete; it checks for instance overlap, for example. All theunifiers recur into kinds:
2

There are actually two variants of zonking in GHC: we can zonk during type-checking or at the end. The
difference between the variants is chiefly what to do for an unfilled unification variable. The former leaves
them alone, while the latter has to default them somehow; details are beyond our scope here.

232

C.2 Today’s GHC• The eager unifier recurs into kinds.
• The unifier in the solver recurs into kinds.
• The pure unifier uses an invariant that the kinds are related before looking at the types.
It must recur when decomposing applications.
In addition, GHC also has an overlap problem within unification, as exhibited in our workby the overlap between rules a-u-kvarL and a-u-kvarR in Figure 7.4. Both the eager unifier
and the constraint-solver unifier deal with this ambiguity by using heuristics to choose which
variable might be more suitable for unification. This particular issue—which variable to unify
when there is a choice—has been the subject of some amount of churn over the years.

C.2.4 Complete User-Supplied Kinds
Along with stand-alone kind signatures, as described in this work, GHC supports complete
user-supplied kinds, or CUSKs. A datatype has a CUSK when certain syntactic conditions are
satisfied; GHC detects these conditions before doing any kind inference. These CUSKs are apoor substitute for proper kind signatures, as the syntactic cues are fragile and unexpected:
users sometimes write a CUSK without meaning to, and also sometimes leave out a necessary
part of a CUSK when they intend to specify the kind. Stand-alone kind signatures are a newfeature; they begin with the keyword type instead of data, as we have used in our work.
Interestingly, it would be wrong to support CUSKs in a system without polymorphic kinds.
Consider this example:
data S1 a = MkT1 S2
data S2 = MkS2 (S1 Maybe)
The types S1 and S2 form a group. We put S2 (which has a CUSK) into the context withkind ?. When we check S1, we find no constraints on a (in the constraint-generation pass;see the general approach below). The kind of S1 is then defaulted to ? → ?. Checking
S2 fails. Instead, we wish to pretend that S2 does not have a CUSK. This would mean that
constraint-generation happens for all the constructors in both S1 and S2, and S1 would get
its correct kind (? → ?) → ?.
With kind-polymorphism, we have no problem because the kind of T1 will be generalized
to ∀(k :: ?). k → ?.
This was reported as a bug #16609.

233

C Kind Inference for Datatypes

C.2.5 Dependency Analysis
The algorithm implemented in GHC for processing datatype declarations starts with dependency analysis, as ours does. The dependency analysis is less fine-grained than what we haveproposed in this work: signatures are ignored in the dependency analysis, and so datatypes
with signatures are processed alongside all the others. This means that the kinds in the example below have more restrictive kinds in GHC than they do in our system:
data S1 :: ∀k. k → ?
data S1 a = MkS1 (S2 Int)
data S2 a = MkS2 (S3 Int)
data S3 a = MkS3 (S1 Int)A naïve dependency analysis would put all three definitions in the same group. The kind for
S1 is given; it would indeed have that kind. The parameters of S2 and S3 would initially havean unknown kind, but when occurrences of S2 and S3 are processed (in the definitions of
S1 and S2, respectively), this unknown kind would become ?. Neither S2 nor S3 would be
generalized.
There is a ticket to improve the dependency analysis: #9427.

C.2.6 Approach to Kind-Checking Datatypes
In GHC’s approach, after dependency analysis, so-called initial kinds are produced for all thedatatypes in the group. These either come from a datatype’s CUSK or from a simple analysisof the header of the datatype (without looking at constructors). This step corresponds to ouralgorithm’s placing a binding for the datatype in the context, either with the kind signatureor with a unification variable (rules a-pgm-dt-ttS and a-pgm-dt-tt).
If there is no CUSK, GHC then passes over all the datatype’s constructors, collecting
constraints on unification variables. After solving these constraints, GHC generalizes thedatatype kind.
For all datatypes, now with generalized kinds, all data constructors are checked (again,
for non-CUSK types). Because the kinds of the types are now generalized, a pass infers any
invisible parameters to polykinded types. For non-CUSK types, this second pass using generalized kinds replaces the Ti 7→ Ti @φci substitution in the context in the last premise to rule apgm-dt-tt. Performing a substitution—instead of re-generating and solving constraints—
may be an opportunity for improvement in GHC.

234

C.2 Today’s GHC

C.2.7 Polymorphic Recursion
One challenge in kind inference is in the handling of polymorphic recursion. Although nonCUSK types are indeed monomorphic during the constraint-generation pass, some limited
form of polymorphic recursion can get through. This is because all type variables are represented by a special form of unification variable called a TyVarTv. TyVarTvs can unify onlywith other type variables. This design is motivated by the following examples:data T1 (a :: k) b = MkT1 (T2 a b)
data T2 (c :: j) d = MkT2 (T1 c d)data T3 a where
MkT3 :: ∀(k :: ?) (b :: k). T3 b
We want to accept all of these definitions. The first two, T1 and T2, form a mutually
recursive group. Neither has a CUSK. However, the recursive occurrences are not polymorphically recursive: both recursive occurrences are at the same kind as the definition. Yet thefirst parameter to T1 is declared to have kind k while the first parameter to T2 is declared
to have kind j. The solution: allow k to unify with j during the constraint-generation pass.
We would not want to allow either k or j to unify with a non-variable, as that would seem togo against the user’s wishes. But they must be allowed to unify with each other to accept thisexample.
With T3 (identical to T from Section 9.4), we have a different motivation. During inference, we will guess the kind of a; call it αb. When checking the MkT3 constructor, we willneed to unify α
b with the locally bound k. We cannot set αb := k, as that will fill α
b with a k,bound to α
b’s right in the context. Instead, we must set k := αb. This is possible only if k isrepresented by a unification variable.
There are two known problems with this approach:
1. It sometimes accepts polymorphic recursion, even without a CUSK. Here is an example:
data T4 a = ∀(k :: ?) (b :: k). MkT4 (T4 b)
The definition of T4 is polymorphically recursive: the occurrence T4 b is specialized
to a kind other than the kind of a. Yet this definition is accepted. The two kinds unify(as k becomes a unification variable, set to the guessed kind of a) during the constraintgeneration pass. Then, T4 is generalized to get the kind ∀k. k → ?, at which point thelast pass goes through without a hitch.

235

C Kind Inference for Datatypes
The reason this acceptance is troublesome is not that T4 is somehow dangerous orunsafe. It is that we know that polymorphic recursion cannot be inferred Henglein[1993], and yet GHC does it. Invariably, this must mean that GHC’s algorithm will be
hard to specify beyond its implementation.
2. In rare cases, the constraint-generation pass will succeed, while the final pass—meant
to be redundant—will fail. Here is an example:
data SameKind :: k → k → Typedata Bad a where
MkBad :: ∀k 1 k 2 (a :: k 1 ) (b :: k 2 ). Bad (SameKind a b)
During the constraint-generation pass, the kinds k 1 and k 2 are allowed to unify, accepting the definition of Bad. During the final pass, however, k 1 and k 2 are proper
quantified type variables, always distinct. Thus the SameKind a b type is ill-kinded
and rejected.
The fact that this final pass can fail means that we cannot implement it via a simple
substitution, as we do in rule a-pgm-dt-tt. One possible solution is our suggestion
to change the scoping of type parameters to GADT-syntax datatype declarations. With
that change, our second motivation above for TyVarTvs would disappear. GHC could
then use TyVarTvs only for kind variables in the head of a datatype declaration, using
proper quantified type variables in constructors. Of course, this change would breakmuch code in the wild, and we do not truly expect it to ever be adopted.

C.2.8 The Quantification Check
Our quantification check (Section 7.7.2) also has a parallel in GHC, but GHC’s solution to the
problem differed from ours. Instead of rejecting programs that fail the quantification check,
GHC accepted them, replacing the variables that would be (but cannot be) quantified with
its constant Any :: ∀k. k. The Any type is uninhabited, but exists at all kinds. As such, it isan appropriate replacement for unquantifiable, unconstrained unification variables. Yet thisdecision in GHC had unfortunate consequences: the Any type can appear in error messages,
and its introduction induces hard-to-understand type errors.
We have later implemented our quantification check in GHC; see #16775.
Another design alternative is to generalize the variable to the leftmost position where it isstill well-formed. Recall the example in Section 7.7.2:

236

C.2 Today’s GHC
data Proxy :: ∀k. k → ?
data Relate :: ∀a (b :: a). a → Proxy b → ?
data T :: ∀(a :: ?) (b :: a) (c :: a) d. Relate b d → ?
b with βb :: a. As there are no further constraints on β,b theWe have d:: αb, and αb = Proxy β,
definition of T is rejected by the quantification check.
Instead of rejecting the program, or solving βb using Any, we can generalize over βb as a
fresh variable f , which is put after a to make it well-kinded. Namely, we getdata T :: ∀(a :: ?) {f :: a} (b :: a) (c :: a) (d :: Proxy f ). Relate @a @f b d → ?
However, this ordering of the variables violates our declarative specification. Moreover,this type requires an inferred variable to be between specified variables. With higher-rankpolymorphism, due to the fact that GHC does not support first-class type-level abstraction(i.e., Λ in types), this type cannot be instantiated to∀(a :: ?) (b :: a) (c :: a) (d :: Proxy b). Relate @a @b b d → ?
or∀(a :: ?) (b :: a) (c :: a) (d :: Proxy c). Relate @a @c b d → ?
which makes the generalization less useful.

C.2.9 ScopedSort
When GHC deals with a local scope—a set of variables that may be reordered—it does a
topological sort on the variables at the end. However, not any topological sort will do: it mustuse one that preserves the left-to-right ordering of the variables as much as possible. This isbecause GHC considers these implicitly bound variables to be specified: they are available
for visible type application. For example, recall the example from Section 7.2.2, modified
slightly:
data Q (a :: (f b)) (c :: k) (x :: f c)Inference will tell us that k must come before f and b, but the order of f and b is immaterial.
Our approach here is to make f , b, and k inferred variables: users of Q will not be able toinstantiate these parameters with visible type application. However, GHC takes a different
view: because the user has written the names of f , b, and k, they will be specified. This choicemeans that the precise sorting algorithm GHC uses to fix the order of local scopes becomes237

C Kind Inference for Datatypes
part of the specification of the language. Indeed, GHC documents the precise algorithm in
its manual. If we followed suit, the algorithm would have to appear in our declarative specification, which goes against the philosophy of a declarative system.
Some recent debate led to a conclusion (see #16726) that we would change the interpretation of the Q example from the main work, meaning that its kind variables would indeed
become inferred. However, the problem with ScopedSort still exists in type signatures, wheretype variables may be implicitly bound.

C.2.10 The “Forall-or-Nothing” Rule
GHC implements the so-called forall-or-nothing rule, which states that either all variables
are quantified by a user-written ∀, or none are. These examples illustrate the effect:
ex1 :: a → b → a
ex2 :: ∀a b. a → b → a
ex3 :: ∀a. a → b → a
ex4 :: (∀a. a → b → a)
The signatures for both ex1 and ex2 are accepted: ex1 quantifies none, while ex2 quantifies
all. The signature for ex3 is rejected, as GHC rejects a mixed economy. However, and perhaps
surprisingly, ex4 is accepted. The only difference between ex3 and ex4 is the seeminglyredundant parentheses. However, because the forall-or-nothing rule applies only at the top
level of a signature, the rule is not in effect for the ∀ in ex4.
This rule interacts with the main work only in that our formalism (and some of our examples) does not respect it. This may be the cause of differing behavior between GHC and theexamples we present.

C.3 Complete Set of Rules
In this section we include missing rules for Chapter 7. Some of the rules are repeated fromthose in the chapter.

C.3.1 Declarative Haskell98Σ `k σ : κ(Kinding for Polymorphic Types)
k-forallΣ, a : κ `k σ : ?
Σ `k ∀a : κ. σ : ?

238

C.3 Complete Set of RulesΣ`Ψ(Well-formed Term Contexts)
ectx-dcon

ectx-empty

Σ`Ψ

Σ`•

Σ `k σ : ?

Σ ` Ψ, D : σ

C.3.2 Algorithmic Haskell98∆ ⊩k σ : κ a Θ(Kinding for Polymorphic Types)
a-k-forall∆ ⊩kv κ∆, a : κ ⊩k σ : κ2 a Θ, a : κ[Θ]κ2 = ?

∆ ⊩k ∀a : κ. σ : ? a Θ∆ ⊩kc σ ⇐ κ(Checking)a-kc-eq∆ ⊩k σ : κ1 a ∆[∆]κ1 = [∆]κ2∆ ⊩kc σ ⇐ κ2
∆ ⊩kv κ(Well-formed Kinds)
a-kv-arrow

a-kv-star∆ ⊩kv κ1

∆ ⊩kv ?

a-kv-kuvar∆ ⊩kv κ2αb∈∆

∆ ⊩kv κ1 → κ2

∆ ⊩kv αb

∆ oka-tctx-empty(Well-formed Type Contexts)
a-tctx-tvar

∆ ok

• ok

a-tctx-tcon
kv

∆⊩ κ

∆ ok

∆, a : κ ok

a-tctx-kuvar
kv

∆⊩ κ

∆, T : κ ok

∆ ok
∆, α
b ok

a-tctx-kuvarSolved∆ ok∆ ⊩kv κ∆, αb = κ ok∆ ⊩ectx Γ(Well-formed Term Contexts)
a-ectx-empty∆ ⊩ectx •a-ectx-dcon∆ ⊩ectx Γ∆ ⊩kc σ ⇐ ?

∆ ⊩ectx Γ, D : σ

239

C Kind Inference for Datatypes

C.3.3 Declarative PolyKinds
Σ ok(Well-formed Type Contexts)
tctx-tvar-tt

tctx-empty• oktctx-tcon-tt

Σ `ela ρ : ?

Σ okΣ `ela η : ?

Σ ok

Σ, a : ρ ok

Σ, T : η okΣ`Ψ(Well-formed Term Contexts)
ectx-dcon-tt

ectx-empty

Σ`Ψ

Σ`•

Σ `ela µ : ?

Σ ` Ψ, D : µ

C.3.4 Algorithmic PolyKinds∆ ⊩inst µ1 : η <: ω ; µ2 a Θ(Instantiation)

a-inst-refla-inst-forall∆ ⊩u ω1 ≈ ω2 a Θ∆, αb : ω1 ⊩inst µ1 @b
α : η[a 7→ αb] <: ω2 ; µ2 a Θ∆ ⊩inst µ : ω1 <: ω2 ; µ a Θ∆ ⊩inst µ1 : ∀a : ω1 . η <: ω2 ; µ2 a Θa-inst-forall-infer∆, αb : ω1 ⊩inst µ1 @b
α : η[a 7→ αb] <: ω2 ; µ2 a Θ∆ ⊩inst µ1 : ∀{a : ω1 }.η <: ω2 ; µ2 a Θ∆ ⊩kc σ ⇐ η ; µ a Θ(Kind Checking)a-kc-sub∆ ⊩k σ : η ; µ1 a ∆1

∆1 ⊩inst µ1 : [∆1 ]η <: [∆1 ]ω ; µ2 a ∆2

∆ ⊩kc σ ⇐ ω ; µ2 a ∆2
∆ ⊩k σ : η ; µ a Θ(Kinding)
a-ktt-var

a-ktt-star

a-ktt-nat∆ ⊩k ? : ? ; ? a ∆∆ ⊩k Int : ? ; Int a ∆a-ktt-tcon(a : ω) ∈ ∆∆ ⊩k a : ω ; a a ∆a-ktt-arrow(T : η) ∈ ∆∆ ⊩k T : η ; T a ∆240∆ ⊩k →: ? → ? → ? ;→a ∆C.3 Complete Set of Rules
a-ktt-forall∆ ⊩kc κ ⇐ ? ; ω a ∆1

∆1 , a : ω ⊩kc σ ⇐ ? ; µ a ∆2 , a : ω, ∆3∆3 ,→ a∆ ⊩k ∀a : κ. σ : ? ; ∀a : ω. [∆3 ]µ a ∆2 , unsolved (∆3 )a-ktt-app∆ ⊩k τ1 : η1 ; ρ1 a ∆1

∆1 ⊩kapp (ρ1 : [∆1 ]η1 ) • τ2 : ω ; ρ a Θ∆ ⊩k τ1 τ2 : ω ; ρ a Θ
a-ktt-foralli∆, αb : ?, a : αb ⊩kc σ ⇐ ? ; µ a ∆2 , a : α
b , ∆3∆3 ,→ a∆ ⊩k ∀a. σ : ? ; ∀a : αb. [∆3 ]µ a ∆2 , unsolved (∆3 )a-ktt-kapp∆ ⊩k τ1 : η ; ρ1 a ∆1[∆1 ]η = ∀a : ω. η2∆1 ⊩kc τ2 ⇐ ω ; ρ2 a ∆2

∆ ⊩k τ1 @τ2 : η2 [a 7→ ρ2 ] ; ρ1 @ρ2 a ∆2
a-ktt-kapp-infer∆ ⊩k τ1 : η ; ρ1 a ∆1i[∆1 ]η = ∀{ai : ωi i }.∀a : ω. η2ii∆1 , αbi : ωi [ ai 7→ αbi ] ⊩kc τ2 ⇐ ω[ ai 7→ αbi ] ; ρ2 a ∆2ii

∆ ⊩k τ1 @τ2 : η2 [ ai 7→ αbi ][a 7→ ρ2 ] ; ρ1 @b
αi @ρ2 a ∆2

∆ ⊩kapp (ρ1 : η) • τ : ω ; ρ2 a Θ(Application Kinding)a-kapp-tt-arrow∆ ⊩kc τ ⇐ ω1 ; ρ2 a Θ∆ ⊩kapp (ρ1 : ω1 → ω2 ) • τ : ω2 ; ρ1 ρ2 a Θ
a-kapp-tt-forall∆, αb : ω1 ⊩kapp (ρ1 @b
α : η[a 7→ αb]) • τ : ω ; ρ a Θ∆ ⊩kapp (ρ1 : ∀a : ω1 . η) • τ : ω ; ρ a Θ
a-kapp-tt-forall-infer∆, αb : ω1 ⊩kapp (ρ1 @b
α : η[a 7→ αb]) • τ : ω ; ρ a Θ∆ ⊩kapp (ρ1 : ∀{a : ω1 }.η) • τ : ω ; ρ a Θ
a-kapp-tt-kuvar∆1 , αb1 : ?, αb2 : ?, αb : ω = (bα1 → αb2 ), ∆2 ⊩kc τ ⇐ αb1 ; ρ2 a Θ∆1 , αb : ω, ∆2 ⊩kapp (ρ1 : αb) • τ : αb2 ; ρ1 ρ2 a Θ241

C Kind Inference for Datatypes∆ ⊩ela µ : η(Elaborated Kinding)a-ela-kuvar

a-ela-star∆ ⊩ela ? : ?

∆ ⊩ela αb : [∆]ωa-ela-tcon

a-ela-vara-ela-nat(b
α : ω) ∈ ∆(a : ω) ∈ ∆∆ ⊩ela Int : ?

ela-arrow∆ ⊩ela a : [∆]ωa-ela-forall∆ ⊩ela ω : ?

(T : η) ∈ ∆∆ ⊩ela T : [∆]η∆ ⊩ela →: ? → ? → ?

∆, a : ω ⊩ela µ : ?

∆ ⊩ela ∀a : ω. µ : ?

a-ela-forall-infera-ela-app∆ ⊩ela ω : ?

∆ ⊩ela ρ1 : ω1 → ω2∆, a : ω ⊩ela µ : ?

∆ ⊩ela ∀{a : ω}.µ : ?

∆ ⊩ela ρ2 : ω1

∆ ⊩ela ρ1 ρ2 : ω2a-ela-kapp-infera-ela-kapp∆ ⊩ela ρ1 : ∀a : ω. η

∆ ⊩ela ρ2 : ω∆ ⊩ela ρ1 : ∀{a : ω}.η∆ ⊩ela ρ1 @ρ2 : η[a 7→ [∆]ρ2 ]∆ ⊩ela ρ2 : ω∆ ⊩ela ρ1 @ρ2 : η[a 7→ [∆]ρ2 ]∆ ⊩genϕc Γ1 ; Γ2

(Generalization)
a-gen

φbci = unsolved(µi )

i

i
c
c
c
bc
∆ ⊩genϕc Di : µi ; D : ∀{φ }.∀{φi }.(µ[φi 7→ φi ])∆ ok(Well-formed Type Contexts)
a-tctx-empty

• ok

∆ ok

a-tctx-tvar-tt∆ ok

a-tctx-tcon-tt∆ ⊩ela ω : ?

∆ ok∆, a : ω ok

a-tctx-kuvar-tt∆ ⊩ela ω : ?
∆, αb : ω oka-tctx-kuvarSolved-tt∆ ok∆ ⊩ela ω2 : [∆]ω1∆, αb : ω1 = ω2 ok
a-tctx-marker∆ ok∆, ▶D ok242i∆ ⊩ela η : ?
∆, T : η ok
a-tctx-lo∆, ∆lo ok∆, {∆lo } ok

C.3 Complete Set of Rules∆ ⊩ectx Γ(Well-formed Term Contexts)
a-ectx-dcon-tt

a-ectx-empty∆ ⊩ectx Γ∆ ⊩ectx •∆ ⊩ela µ : ?

∆ ⊩ectx Γ, D : µ∆ ⊩u ω1 ≈ ω2 a Θ(Unification)a-u-appa-u-refl-tt∆ ⊩u ρ1 ≈ ρ3 a ∆1

∆ ⊩u ω ≈ ω a ∆∆1 ⊩u [∆1 ]ρ2 ≈ [∆1 ]ρ4 a Θ∆ ⊩u ρ1 ρ2 ≈ ρ3 ρ4 a Θ

a-u-kapp∆ ⊩u ρ1 ≈ ρ3 a ∆1

∆1 ⊩u [∆1 ]ρ2 ≈ [∆1 ]ρ4 a Θ∆ ⊩u ρ1 @ρ2 ≈ ρ3 @ρ4 a Θ
a-u-kvarL-tt∆ `prb : ω1 , Θ 2
αb ρ1 ⇝ ρ2 a Θ1 , αΘ1 ⊩ela ρ2 : ω2

Θ1 ⊩u [Θ1 ]ω1 ≈ ω2 a Θ3

∆ ⊩u αb ≈ ρ1 a Θ3 , αb : ω 1 = ρ2 , Θ 2
a-u-kvarR-tt∆ `prb : ω1 , Θ 2
αb ρ1 ⇝ ρ2 a Θ1 , αΘ1 ⊩ela ρ2 : ω2

Θ1 ⊩u [Θ1 ]ω1 ≈ ω2 a Θ3

∆ ⊩u ρ1 ≈ αb a Θ3 , αb : ω 1 = ρ2 , Θ 2
a-u-kvarL-lo-tt∆1 , ∆2 ++mv α
b : ω1 ; ΘΘ1 , {Θ2 } ⊩ela ρ2 : ω2∆[{Θ}] `prb : ω1 , Θ3 }, Θ4αb ρ1 ⇝ ρ2 a Θ1 , {Θ2 , α
Θ1 , {Θ2 } ⊩u [Θ1 , Θ2 ]ω1 ≈ ω2 a Θ5 , {Θ6 }∆[{∆1 , αb : ω1 , ∆2 }] ⊩u αb ≈ ρ1 a Θ5 , {Θ6 , αb : ω1 = ρ2 , Θ3 }, Θ4a-u-kvarR-lo-tt∆1 , ∆2 ++mv α
b : ω1 ; ΘΘ1 , {Θ2 } ⊩ela ρ2 : ω2∆[{Θ}] `prb : ω1 , Θ3 }, Θ4αb ρ1 ⇝ ρ2 a Θ1 , {Θ2 , α
Θ1 , {Θ2 } ⊩u [Θ1 , Θ2 ]ω1 ≈ ω2 a Θ5 , {Θ6 }∆[{∆1 , αb : ω1 , ∆2 }] ⊩u ρ1 ≈ αb a Θ5 , {Θ6 , αb : ω1 = ρ2 , Θ3 }, Θ4∆ `prαb ω1 ⇝ ω2 a Θa-pr-star∆ `prαb ?⇝?a∆a-pr-nat∆ `prαb Int ⇝ Int a ∆(Promotion)a-pr-arrow

a-pr-tcon∆ ⊩prαb →⇝→a ∆a-pr-app∆ `prαb ω1 ⇝ ρ1 a ∆1∆[T ][bα] `prα]αb T ⇝ T a ∆[T ][b∆1 `prαb [∆1 ]ω2 ⇝ ρ2 a Θ∆ `prαb ω1 ω2 ⇝ ρ1 ρ2 a Θ

243

C Kind Inference for Datatypes
a-pr-kapp∆ `prαb ω1 ⇝ ρ1 a ∆1

∆1 `prαb [∆1 ]ω2 ⇝ ρ2 a Θ∆ `prαb ω1 @ω2 ⇝ ρ1 @ρ2 a Θa-pr-tvar∆[a][bα] `prα]αb a ⇝ a a ∆[a][ba-pr-kuvarR-tt

a-pr-kuvarL∆ `prα][βb : ρ]αb [∆]ρ ⇝ ρ1 a Θ[bb α] `pr βb ⇝ βb a ∆[β][bb α]∆[β][b
α
b

b
b
b
∆[b
α][βb : ρ] `prb][βb : ρ = βb1 ]αb β ⇝ β1 a Θ[β1 : ρ1 , α∆1 ++mv ∆2 ; Θ(Moving)a-mv-kuvar

a-mv-empty

var(ω) ] dom(∆2 )• ++mv ∆ ; ∆∆1 ++mv ∆2 ; Θαb : ω, ∆1 ++mv ∆2 ; αb : ω, Θa-mv-kuvarM∆1 ++mv ∆2 , αb:ω;Θ

¬(var(ω) ] dom(∆2 ))αb : ω, ∆1 ++mv ∆ ; Θ
a-mv-tvar∆1 ++mv ∆2 ; Θvar(ω) ] dom(∆2 )a : ω, ∆1 ++mv ∆2 ; a : ω, Θ
a-mv-tvarM∆1 ++mv ∆2 , a : ω ; Θ¬(var(ω) ] dom(∆2 ))a : ω, ∆1 ++mv ∆2 ; Θ

C.3.5 Context Application in PolyKinds[∆]η applies ∆ as a substitution to η.

244

[∆]?

=

?

[∆]Int

=

Int

[∆]a

=

a

[∆]T

=

T

[∆] →

=

→

[∆]∀a : ω. η=

∀a : [∆]ω. [∆]η[∆]∀{a : ω}.η=∀{a : [∆]ω}.[∆]η[∆](ρ1 ρ2 )=([∆]ρ1 ) ([∆]ρ2 )[∆](ρ1 @ρ2 )=([∆]ρ1 ) @([∆]ρ2 )

[∆[b
α]]b
α

=

α
b

[∆[b
α : ω = ρ]]b
α

=

[∆[b
α : ω = ρ]]ρ

C.3 Complete Set of Rules[∆]Γ applies ∆ as a substitution to Γ.
[Ω]•

=

•

[Ω](Γ, D : µ)=[Ω]Γ, D : [Ω]µ[Ω]∆ applies Ω as a substitution to ∆.
[Ω]•

=

•

[Ω, a : ω](∆, a : ω)=[Ω]∆, a : [Ω]ω[Ω, T : ω](∆, T : ω)=[Ω]∆, T : [Ω]ω[Ω, αb : ω = ρ](∆, αb : ω)

=

[Ω]∆

[Ω, α
b : ω = ρ1 ](∆, αb : ω = ρ2 )

=

[Ω]∆

if [Ω]ρ1 = [Ω]ρ2[Ω, αb : ω = ρ]∆

=

[Ω]∆

if α
b∈
/∆

[Ω, ▶D ](∆, ▶D )

=

[Ω]∆

[Ω, {Ω1 }](∆, {∆1 })=[Ω, Ω1 ](∆, ∆′ )where ∆′ = topo (∆1 )C.3.6 Context Extension in PolyKinds∆ −→ Θ

(Context Extension)

a-ctxe-empty• −→ •

a-ctxe-tvar-tt

a-ctxe-tcon-tt∆ −→ Θ

∆ −→ Θ

∆, a : ω −→ Θ, a : ω∆, T : η −→ Θ, T : ηa-ctxe-kuvarSolved-tt

a-ctxe-kuvar-tt∆ −→ Θ

∆ −→ Θ
∆, αb : ω −→ Θ, α
b:ω

∆, α
b : ω = ρ1 −→ Θ, αb : ω = ρ2a-ctxe-solve-tt∆ −→ Θ

[Θ]ρ1 = [Θ]ρ2a-ctxe-add-tt

Θ ⊩ela ρ : [Θ]ω∆ −→ Θ

Θ ⊩ela ω : ?

∆, αb : ω −→ Θ, αb:ω=ρ∆ −→ Θ, αb:ω

a-ctxe-addSolved-tt

a-ctxe-marker∆ −→ ΘelaΘ ⊩ ρ : [Θ]ω∆ −→ Θ, αb:ω=ρ∆ −→ Θ
∆, ▶D −→ Θ, ▶D

a-ctxe-lo∆ −→ Θ

∆, topo (∆1 ) −→ Θ, Θ1∆, {∆1 } −→ Θ, {Θ1 }245