
ICFP: G: Type Inference for Disjoint Intersection Types Birthe van den Berg, KU Leuven, Belgium∗ [email protected] 1 Problem and Motivation be somewhat arbitrary and unexpected. Hence, a more ex- Intersection types [14, 34] were first introduced in the early plicit approach prevents incoherence by rejecting programs 1980s in a theoretical pursuit to characterize all strongly nor- with multiple meanings. This is the approach taken by dis- malizing λ-calculus terms. Yet, in recent years they have risen joint intersection types [30], which we build on in this work. to practical prominence as part of industrial programming They require the types A and B of the two terms in a merge languages like Scala and its DOT-calculus [2], Microsoft’s to be disjoint, denoted A ∗ B. If the disjointness requirement TypeScript, Facebook’s Flow and Red Hat’s Ceylon. is violated, as in the example above where Int ∗ Int does not The intersection of types A and B, denoted A&B, is the hold, the program is rejected, explicitly asking the program- type of all values that have at the same time type A and mer to manually resolve the issue. type B. They are a natural fit for languages that feature both Disjoint intersection types are also compatible with pow- subtyping and advanced forms of inheritance, like multiple erful BCD-style subtyping [4] and parametric polymorphism inheritance, traits or mixins. Consider the following Scala [1]. This makes them able to express advanced subtyping example where openAndShow invokes the open and show features—like nested composition [15, 19], which is a key as- methods on its parameter x: pect of family polymorphism [18] and scalable extensibility— as well as a concise solution to the expression problem. Un- def openAndShow(x: Openable & Showable) fortunately using disjoint intersection types for these and = { x.open() ; x.show() } other applications can be daunting as programmers have to The two invoked methods are defined in separate traits add ample type and disjointness annotations to their pro- trait Openable { def open(): Unit } grams in order to help the type checker. trait Showable { def show(): Unit } This work aims to unburden the programmer and make Hence, x should have both types Openable and Showable. disjoint intersection types more accessible by providing a These two type requirements are combined in the intersec- type inference algorithm that automatically derives the nec- tion type Openable & Showable. The class Door inherits essary type and disjointness information. Our algorithm both traits and implements both methods: should do for disjoint intersection types what the famous Algorithm W does for the Hindley-Milner type system [21, class Door extends Openable with Showable { 26, 27]. What makes our setting much more challenging def open() { println("door opens") } is the combination of intersection types, disjointness con- def show() { println("door shows") } straints, BCD subtyping and parametric polymorphism. As } far as we know, no prior work considers the combination This makes the call openAndShow(new Door) valid. of these features and can unlock type inference for modern The merge operator [17] x;;y provides a primitive nota- programming languages with advanced inheritance features. tion for introducing intersection types. For instance, (true;; 5) In short, the problem statement of this paper is: has type Bool & Int and can behave as a Bool value or an Intvalue to suit the context it is used in: How to do type inference for disjoint intersection types? (true ,, 5) OR false = true This work is based on my master’s thesis. (true ,, 5) + 3 = 8 If not treated carefully, the merge operator can merge terms 2 Background and Related Work + of overlapping types, making the semantics ambiguous, for 2.1 History of the Fi -calculus example: Figure1 shows an historical overview of how intersection + (1 ,, 5) + 3 = 6 types evolved into the Fi -calculus. (1 ,, 5) + 3 = 8 Coppo [14] and Pottinger [34] were the first to introduce The Scala and Typescript counterparts of the merge operator intersection types to characterize all strongly normalizing deal with this ambiguity by picking the first component of λ-terms. Intersection types increase the expressiveness of the intersection in case of overlap. This automatic choice can types but in many languages, such as Ceylon [28], intersec- tions of disjoint types are uninhabited. In order to also in- ∗Advisor: Tom Schrijvers crease the expressiveness of terms, Reynolds [36] introduced Conference’17, July 2017, Washington, DC, USA B. van den Berg Figure 1. Timeline of the evolution of calculi with intersection types. a restricted version of a merge operator for the Forsythe on which fields are shown or hidden. Therefore, whenever a language, which for instance forbids intersections of two value of a certain record type is expected, a record contain- function types. Castagna et al. [11] also studied a merge-like ing more information than necessary can be supplied [33]. operator for functions in their language λ&. Polymorphic records are supported by row polymorphism In 2014, Dunfield [17] introduced a calculus λ;; with unre- and by structural subtyping. The extra information of poly- stricted intersections, unions and merges. Oliveira et al. [30] morphic records, which is implicitly included in the records, transformed this calculus into a coherent equivalent λi by can take three forms: absent, present A, or row variable introducing disjoint intersection types. In 1983, Barendregt ρ, indicating that the field information is unknown. Row et al. introduced BCD subtyping [4] in their BCD type system variables cannot contain fields with a label that is already with distributivity of the intersection operator over function present in the record. Similarly, in type fl : Ag & B [1], type B types. Blaauwbroek [9] extended λ;; with BCD subtyping in can be considered a row variable. The disjointness constraint _ + his calculus λ^. In λi [7] these ideas were combined into a co- ensures that values of type B do not contain a record with herent calculus with disjoint and unrestricted intersections type fl : Ag. featuring BCD subtyping. Many features of row polymorphism can be simulated When combining subtyping with polymorphism, the most with disjoint polymorphism. Harper and Pierce have de- common procedure is to use bounded quantification, e.g. signed λ j j [20], which is essentially System F with row poly- 8(α <: Int):α ! α, of which F<: [10] in 1985 defines a morphism and a merge operator jj. It uses constrained quan- model. Pierce [32] claimed that bounded quantification can tification to avoid concatenating records with a common field be encoded in terms of intersection types and polymorphism label and allows merging records with statically unknown and proposed the F^ calculus, in which the above type is fields, for example: equivalent to 8α:(α ^ Int) ! (α ^ Int). mergeRcd = Λα #Empty:Λα #α :λx : α :λx : α :x jjx Disjoint intersection types deserve special attention when 1 2 1 1 1 2 2 1 2 used in combination with parametric polymorphism. This where the type variables lack type information, but the com- was apparent to Alpuim et al. [1] who extended λi with patibility (i.e. no common record labels) is guaranteed by the polymorphism to their calculus Fi , featuring disjoint poly- constraints in the quantification. This can easily be translated morphism. Disjoint quantification includes disjointness re- to a calculus with disjoint quantification: quirements at the term and type level in the introduction of mergeRcd’ = Λ(α ∗ >):Λ(α ∗ α ):λx : α :λx : α :x ;; x type variables, generalizing System F univeral quantification, 1 2 1 1 1 2 2 1 2 e.g. 8(α ∗ Int):α & Int. The empty constraint is denoted by The ideas of row polymorphism were later adopted into disjointness with >. type systems for extensible records [20, 24]. Type inference + + is supported for various row type systems such as those of Combining the ideas of λi and Fi led to the design of Fi [8], a calculus with disjoint and unrestricted intersections, Wand [39, 40], Leijen [24] or Rémy [35]. disjoint polymorphism and BCD subtyping; and SEDEL [6], + 2.3 Intersection Types and Type Inference a source language built on top of Fi . An intersection of types A and B can be understood from 2.2 Row polymorphism three perspectives [5]: Row types were first introduced by Wand [40] to model in- • Set-theory: containing all elements of A that are also heritance, presenting the concept of row variables to allow elements of B, an incremental construction of record types. Consider poly- • Type-theory: being a subtype of A as well as of B, morphic records: records that have many types, depending • Order-theory: greatest lower bound of A and B. ICFP: G: Type Inference for Disjoint Intersection Types Conference’17, July 2017, Washington, DC, USA In this sense, intersection types can also be considered an those of the form A & B <: C. option for type polymorphism. Every finite-rank25 [ ] restric- The merge operator allows to explicitly introduce values of tion of calculi with intersection types has principal typings types with different type constructors, e.g., (Int ! Int) & Bool. and decidable type inference [23]. Rank-N intersection types, Most calculi lack support for this operator because it is not for a fixed value of N, refer to types in which an intersection well-studied and complicates getting coherent semantics [17]. does not appear in the domain of N or more function types However, it enables a powerful type system with function [31]. For instance, ((Int&Bool) ! Int) ! Bool is a rank-3 overloading, e.g., ((λx ! x + 1);; (λx ! x > 0)) 3 which intersection type. Furthermore, parametric polymorphism has as result 4;; true of type Int & Bool. should be restricted to prenex predicative polymorphism Also distributivity is seldomly used in calculi with a merge in order to have decidable type inference.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages5 Page
-
File Size-