<<

Giving Haskell a Promotion

Brent A. Yorgey Julien Cretin Simon Peyton Jones Stephanie Weirich INRIA Dimitrios Vytiniotis University of Pennsylvania [email protected] Microsoft Research Cambridge {byorgey,sweirich}@cis.upenn.edu {simonpj,dimitris}@microsoft.com

Jose´ Pedro Magalhaes˜ Utrecht University [email protected]

Abstract But, embarrassingly, type-level programming in Haskell is al- Static type systems strive to be richly expressive while still being most entirely untyped, because the kind system has too few kinds simple enough for programmers to use. We describe an experiment (?, ? → ?, and so on). Not only does this prevent the programmer that enriches Haskell’s kind system with two features promoted from properly expressing her intent, but stupid errors in type-level from its : data types and polymorphism. The new sys- programs simply cause type-level evaluation to get stuck rather tem has a very good power-to-weight ratio: it offers a significant than properly generating an error (see §2). In addition to being too improvement in expressiveness, but, by re-using concepts that pro- permissive (by having too few kinds), the kind system is also too grammers are already familiar with, the system is easy to under- restrictive, because it lacks polymorphism. The lack of kind poly- stand and implement. morphism is a well-known wart; see, for example, Haskell’s fam- ily of Typeable classes (§2.5), with a separate (virtually identical) Categories and Subject Descriptors D.3.3 [Language Constructs class for each kind. and Features]: Data types and structures, Polymorphism; F.3.3 In this paper we describe a design that fixes these problems, [Studies of Program Constructs]: Type structure and we sketch its implementation in GHC, our Haskell compiler. Our design is inspired by Conor McBride’s Strathclyde Haskell General Terms Languages, Design Enhancement (SHE) preprocessor [11], which automatically pro- motes datatypes to be datakinds. Our work goes well beyond SHE Keywords Haskell, promotion, kinds, polymorphism by also introducing kind polymorphism, and integrating these two new features with convenient source syntax and full type (and kind) 1. Introduction inference. From a type-theoretic point of view, this paper has little new to Static type systems are the world’s most successful application say: full-spectrum dependently typed languages like Coq [22] or of formal methods. Types are simple enough to make sense to Agda [14] are more expressive still. But, as we discuss in more de- programmers; they are tractable enough to be machine-checked on tail in §7, these languages are in some ways too powerful: they have every compilation; they carry no run-time overhead; and they pluck a high barrier to entry both for programmers (who want to write a harvest of low-hanging fruit. It makes sense, therefore, to seek to programs in natural and straightforward ways) and implementors build on this success by making the type system more expressive (who have to worry about efficiently executing these programs). without giving up the good properties we have mentioned. Instead, we start from the other end: we carefully extend a state- Every static type system embodies a compromise: it rejects of-the-art functional with features that ap- some “good” programs and accepts some “bad” ones. As the pear in dependently-typed languages. Our primary audience is the dependently-typed programming community knows well, the abil- community of Designers and Implementors of Typed Languages, to ity to express computation at the type level can improve the “fit”; whom we offer a big increase in expressive power for a very modest for example, we might be able to ensure that an alleged red-black cost in terms of intellectual and implementation complexity. tree really has the red-black property. Recent innovations in Haskell Specifically, our contributions are: have been moving in exactly this direction. Notably, GADTs [15] and type families [17] turn the type system into a (modest) pro- • We extend Haskell with a rich kind system, including kind poly- gramming language in its own right. morphism. Suitable value and type constructors are automati- cally promoted to become type and kind constructors, respec- tively (we explain precisely which constructors can be lifted in §3.3). We show, by example, that these modest extensions offer a large gain in expressiveness (§2). Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed • We formalize an explicitly-typed intermediate language, Sys- for profit or commercial advantage and that copies bear this notice and the full citation ↑ tem FC (pronounced “FC-pro”), that embodies the new kind on the first page. To copy otherwise, to republish, to post on servers or to redistribute ↑ to lists, requires prior specific permission and/or a fee. system (§3). FC is no toy: we have implemented it in GHC as TLDI’12, January 28, 2012, Philadelphia, PA, USA. a modest extension of GHC’s existing intermediate language, Copyright © 2012 ACM 978-1-4503-1120-5/12/01. . . $10.00 System FC. Our extension to this intermediate language guides our overall design—going further would require much more In our new system the above example is now valid Haskell! significant effort. In the declaration of Vec we can see that Nat is used as a kind. ↑ Following SHE, we achieve this effect by automatically promoting • GHC uses F throughout its optimization phases, and each op- C (suitable) datatypes to become kinds; and their data constructors timization must ensure that the transformed program is well- (Zero and Succ in this case) to become type-level data. In effect typed. So the metatheory of F↑ is of practical importance, es- C this gives the Haskell programmer the ability to declare their own pecially subject reduction (§4). In addition to subject reduction kinds, by declaring datatypes and promoting them up a level. we prove progress, which ensures that coercions can be erased These datakinds may also be used in GHC’s indexed type fami- at runtime without compromising type safety. lies (i.e. type functions). For example, • We describe the modifications to GHC’s type inference engine (§5) and the source language extensions (§6) to support the Plus (a :: Nat)(b :: Nat) :: Nat new features. (Haskell is a large language, so these sections are type instance Plus Zero b = b necessarily informal.) type instance Plus (Succ a) b = Succ (Plus a b) In general, a suitable (§3.3) datatype declaration Finally, we discuss related work (§7) as well as directions for future research (§8). data T = C1 T1 | C2 T2 | ... Our goal throughout is to provide maximum gain for minimum not only defines a T and data constructors C1, C2, pain. One could go further in terms of supported features, but . . . , but also a kind T, a type constructor C1 of kind T1 → T we believe we have found a sweet spot. The new extensions are (using the promoted kinds T and T1), and similarly for the other fully implemented, and will soon be released as part of GHC. The constructors. experience of GHC’s users will then inform our understanding of possible future developments in the design space of dependently- 2.2 Resolving namespaces typed programming languages. Although in principle a simple idea, in practice this approach leads to an awkward naming problem for source Haskell programs, be- 2. Typed type-level programming cause Haskell allows type constructors and data constructors to We begin with an informal motivation for the extensions of our have the same name: system. data T = T Int 2.1 Promoting datatypes If we see “T” in a type, does it refer to the type T (of kind ?) or the Consider the following standard implementation of length-indexed promoted data constructor T (of kind Int → T)? In such cases, we vectors in Haskell: adopt the following notation: plain T means the type constructor, while 'T, with a prefixed single quote, denotes the promoted data data Zero constructor. So the fully explicit version of the foregoing example data Succ n looks like this: data Vec :: ? → ? → ? where data Nat = Zero | Succ Nat Nil :: Vec a Zero data Vec :: ? → Nat → ? where :: a → Vec a n → Vec a (Succ n) VNil :: Vec a 'Zero We declare two empty datatypes, Zero and Succ, to serve as VCons :: a → Vec a n → Vec a ('Succ n) “type-level values”, and use a generalized algebraic datatype (GADT) [15] to define a type Vec whose first argument is the type Where the promotion is unambiguous, the quote may be omitted. of its elements and whose second argument is a type-level natural We do not require (or allow) the quote notation in kinds, be- number reflecting the length of its values. cause there is no ambiguity to resolve. We do not allow promoting However, for a language like Haskell with a strong, static type types which are themselves indexed by a promoted data constructor ? system, this example is rather embarrassing: it is untyped! The (§3.3), so anything in a kind other than can only be a promoted type parameter to Succ has kind ?, which gives us no indication type. that we intend for it to always be a type-level representation of a 2.3 Kind polymorphism for promoted types natural number. The same is true of the second parameter to Vec. Essentially, ? is serving as the single type in a “uni-typed” type We also allow promoting parameterized datatypes, such as lists. system.1 We are not prevented from writing nonsensical types such For example: as Succ Bool or Vec Zero Int. data HList :: [?] → ? where It would be much better to be able to write HNil :: HList '[] data Nat = Zero | Succ Nat HCons :: a → HList as → HList (a : as) data Vec :: ? → Nat → ? where Here we have declared HList, the type of heterogeneous singly- VNil :: Vec a Zero linked lists. The index of HList is a list of types which records the VCons :: a → Vec a n → Vec a (Succ n) types of the elements (created by promoting the list datatype). For example, This is the sort of thing we could write in a language with a full- spectrum system, such as Agda [14]. We have de- HCons "Hi" (HCons True HNil) :: HList (String : Bool : '[ ]) clared a normal datatype Nat, and Vec takes as its second argument a value of type Nat. Our intention for the arguments of Vec is now is a heterogeneous list containing two values of different types. clear, and writing Vec Zero Int will rightly yield a type error. Haskell allows the syntactic sugar [a, b ] for the explicit list (a : b : [ ]), and we support the same sugar in types, thus: 1 Well, not quite uni-typed, because we have arrow kinds, but close. HCons "Hi" (HCons True HNil) :: HList '[String, Bool] The prefix quote serves, as before, to distinguish the type-level list and can now be used to construct recursive types indexed on kinds from the list type in, say reverse :: [a ] → [a ]. other than ?. For example, here is an explicit construction of the If this promotion is to be allowed, what is the kind of the Vec datatype from §2.2: promoted data constructor '(:)? Since the data constructor (:) is data VecF (a :: ?)(f :: Nat → ?)(n :: Nat) where type-polymorphic, the promoted constructor '(:) must be kind- VFNil :: VecF a f Zero polymorphic: VFCons :: a → f n → VecF a f (Succ n) '(:) :: ∀X . X → [X ] → [X ] type Vec a n = Mu (VecF a) n where X is a kind variable. This time, Mu is instantiated to ((Nat → ?) → (Nat → ?)) → We have by now seen the most significant modifications to the (Nat → ?). kind language. Haskell kinds now include the kind of types of val- ues, written ?, other base kinds formed from promoted datatypes, 2.5 Kind polymorphism for classes arrow kinds, and polymorphic kinds. This means that we can only promote data constructors with types analogous to one of these Classes can also usefully be kind-polymorphic. This allows us, for kinds. In particular, we will not promote data constructors with example, to clean up Haskell’s family of Typeable classes, which constrained types (including GADTs) or higher-order type poly- currently look like this: morphism. We return to this issue in §3.3. class Typeable (a :: ?) where typeOf :: a → TypeRep 2.4 Kind polymorphism for datatypes class Typeable1 (a :: ? → ?) where Kind polymorphism is useful independently of promotion. Con- typeOf1 :: ∀b. a b → TypeRep sider the following datatype declaration: ... and so on ... data TApp f a = MkTApp (f a) The lack of kind polymorphism is particularly unfortunate here; This code has always been legal Haskell, but, lacking kind poly- the library has only a fixed, ad hoc, collection of Typeable classes. morphism, the kind of TApp was defaulted to (? → ?) → ? → ? With kind polymorphism we can write [10, Section 4.6]. However, TApp is naturally kind-polymorphic; class Typeable a where just as we do type generalization for term definitions, we now also typeOf :: Proxy a → TypeRep do kind generalization for type definitions. The kind of TApp is thus inferred as ∀X . (X → ?) → X → ?. (Like all datatypes, the We have generalized in two ways here. First, Typeable gets a 3 result of TApp must still be ?.) polymorphic kind: Typeable :: ∀X . X → Constraint , so that it A less contrived example is the following GADT, used to reify can be used for types of any kind. Second, we need some way to proofs of type equality: generalize the argument of typeOf , which we have done via a poly- kinded Proxy: data EqRefl a b where Refl :: EqRefl a a data Proxy a = Proxy The kind of EqRefl also used to default to (? → ? → ?); that The idea is that, say typeOf (Proxy :: Proxy Int) will return the is, EqRefl could only express equality between types of values. type representation for Int, while typeOf (Proxy :: Proxy Maybe) To express equality between type constructors, such as Maybe, will do the same for Maybe. The proxy argument carries no required tediously declaring a separate EqRefl-like datatype for information—the type has only one, nullary constructor—and is each kind, with the only difference being kind annotations on only present so that the programmer can express the type at which EqRefl’s type parameters. However, EqRefl is now inferred to have to invoke typeOf . Because there are no constraints on the kind of the polymorphic kind ∀X . X → X → ?, so it can be used a, it is safe to assign Proxy the polymorphic kind ∀X . X → ?. equally well on any two types of the same kind. We also allow The user is allowed to provide instances of the class Typeable the programmer to write for specific kind and type instantiations, such as: data EqRefl (a :: X )(b :: X ) where instance Typeable Int where Refl :: ∀X . ∀(a :: X ). EqRefl a a typeOf = ... -- some representation for Int instance Typeable Maybe where if they wish to indicate the kind polymorphism explicitly. typeOf = ... -- some representation for Maybe A final example of a datatype definition that benefits from kind polymorphism is a higher-kinded fixpoint operator [? ] Even though the class declaration is kind polymorphic, the in- stances need not be. data Mu f a = Roll (f (Mu f ) a). 2.6 Kind polymorphism for terms Mu can be used to explicitly construct polymorphic recursive types from their underlying functors2; for instance: So far, we have given examples of types with polymorphic kinds. It is also useful to have terms whose types involve kind polymor- data ListF f a = Nil | Cons a (f a) phism. We have already seen several, since data constructors of type List a = Mu ListF a kind-polymorphic data types have kind-polymorphic types, thus: Previously, the kind of Mu would have been defaulted to ((? → Proxy :: ∀X . ∀(a :: X ). Proxy a ?) → (? → ?)) → (? → ?); with the addition of kind polymor- Refl :: ∀X . ∀(a :: X ). EqRefl a a phism, Mu is given the polymorphic kind Here is a more substantial example, taken from McBride [12]. Mu :: ∀X . ((X → ?) → (X → ?)) → (X → ?) Consider, first, the type constructor (:→) defined as follows:

2 The cognoscenti will know that lists can be expressed with a simpler, first- 3 The Constraint kind is a new base kind introduced to classify the types order fixpoint operator. We use a higher-kinded one here because it can also of evidence terms, such as dictionaries—more about this feature handle an indexed type such as Vec. in §3. type s :→ t = ∀i. s i → t i 2.8 Summary If s, t :: κ → ? are type constructors indexed by types of kind κ, So far, we have demonstrated the nature of programs that can be then s :→ t is the type of index-preserving functions from s to t. written with our two new features: For example, consider the function vdup which duplicates each • Automatic promotion of datatypes to be kinds and data con- element of a length-indexed vector, guaranteeing to preserve the structors to be types. length of the vector: • Kind polymorphism, for kinds, types (including kind-indexed vdup :: Vec a n → Vec (a, a) n type families and type classes), and terms. vdup VNil = VNil vdup (VCons a as) = VCons (a, a)(vdup as) The former allows us to give informative kinds to types, thereby excluding bad programs that were previously accepted. The latter Using the type synonym above, vdup’s type can be rewritten as accepts a wider class of good programs, and increases re-use, just vdup :: Vec a :→ Vec (a, a). like type polymorphism. Both features are integrated with type and kind inference. McBride observes that it is possible (and useful) to define func- Our extensions not only enable programmers to write new and tors that lift index-preserving functions (like vdup) from one in- interesting programs, but also help clean up existing libraries. For dexed (such as Vec a n) to another (such as square matrices instance, the HList library [9] may be entirely rewritten without Vec (Vec a n) n). He introduces the notion of an indexed functor code duplication and extra type classes to track well-kindedness. Similarly, the Scrap Your Boilerplate library [?? ] can benefit from class IFunctor f where the kind-polymorphic Typeable class described previously. imap :: (s :→ t) → (f s :→ f t) and goes on to present several interesting instances of this class. 3. ↑ Now, what are the kinds of s, t, and f ? A of thinking (or C simply running GHC’s type checker) reveals that there must be In this section we present the extensions to GHC’s intermediate kinds X1 and X2 such that s, t, and f have the kinds language, System FC [21], that are necessary to support these new source language features. System FC is an explicitly typed interme- s, t :: X1 → ? diate language which has simple syntax-directed typing rules and is f :: (X1 → ?) → (X2 → ?) robust to program transformation. Through the mechanism of type All three are mentioned in the type of imap, which ought to be (and now kind) inference, source Haskell programs are elaborated to well-typed System F programs, a process that guarantees the polymorphic over X1 and X2. The full type of imap must therefore C be soundness of type inference. This section presents the technical details of System FC and its ∀X1 X2. ∀f :: (X1 → ?) → (X2 → ?). ∀s, t :: X1 → ?. extensions, to provide a semantics for the new features. Because FC is a small language, it allows us to make precise exactly what our IFunctor f ⇒ (s :→ t) → (f s :→ f t). new design does and does not support. Moreover, System FC has a Note that this type involves two different sorts of ∀ abstractions: the straightforward metatheory, so we can justify our design decisions first abstracts over the kinds X1 and X2, whereas the others abstract by demonstrating that our additions do not complicate reasoning over the type constructors s, t, and f . about the properties of the system (§4). As an aside, this example highlights an interesting difference For clarity, we call the extended language of this paper System ↑ ↑ between FC and SHE [11], which restricts the kind of f to FC (pronounced “FC-pro”), reserving the name System FC for the prior version of the language. f :: ('a → ?) → ('b → ?). ↑ That is, using SHE, f can only transform types indexed by some 3.1 System FC overview promoted kinds, whereas in our implementation f can transform The expression syntax of System F↑ is given in Figure 1, with types indexed by arbitrary kinds. This is certainly no failing of C the differences from System FC highlighted. As the language is SHE, which places this restriction on kind polymorphism for the explicitly typed, this syntax references kinds (κ) and types (τ), sensible reason that promoted kinds can all be “erased” to ?, and which appear in Figure 3 and Figure 4. as a textual preprocessor SHE cannot be expected to do much else. The syntax also mentions coercions (γ), which explicitly en- However, this does show one of the advantages of a natively imple- code type equalities arising from type family instance declarations mented, strongly typed implementation over a textual preprocessor. and GADT constructors (plus a Haskell-specific form of type gen- erativity, newtype declarations). For example, each type family 2.7 Kind-indexed type families instance declaration gives rise (through elaboration) to an equal- In GHC type families are type-indexed. With the addition of kind- ity axiom that equates the left and the right-hand side of the in- polymorphism we may now write kind- and type-indexed families, stance declaration. Such axioms can be composed and transformed as the example below demonstrates: to form coercions between more complex types. The role of coer- type family Shape (a :: X ) :: ? cions is not central to this paper, so we refer the reader to related work for more details on motivation and uses of coercions [21, 24], type instance Shape (a :: ?) = a or the foundations of type equalities in [? ]. type instance Shape (a :: ? → X ) = Shape (a ()) ↑ This abstract syntax of FC makes no mention of the quote- The above type family is an example of -, marks of §2.2 because the quotes are required only to resolve where Shape t denotes the application of the type constructor t to ambiguity in the concrete syntax of Haskell. As a convention, we as many copies of the as its kind allows. This form of kind use overline notation to stand for a list of syntactic elements, such indexing allows for code reuse, since without it the programmer as the branches of a case expression p → u. Multi-substitution, would have to declare separate type families at each kind, in a such as τ [σ/a], is only well-defined when the lists have the same manner similar to the current treatment of the Typeable class. length. e, u ::= Expressions Expression typing The typing judgement for terms is syntax- | x Variables directed and largely conventional; Figure 2 gives the typing rules | λx: τ. e | e1 e2 Abstraction/application for some of the novel syntactic forms. Explicit type coercions of | Λa: κ. e | e τ Type abstraction/application the form e . γ are used to cast the type of an expression e from | λc: τ. e | e γ Coercion abstraction/application τ1 to τ2, given γ, a proof that the two types are equal. Note that | ΛX . e Kind abstraction although these coercions are explicit in the intermediate language, | e κ Kind application | K Data constructors they have no runtime significance and are erased by GHC in a later | case e of p → u Case analysis compilation stage. | e . γ Casting Rule T CASE is used to typecheck pattern matching expressions and will be discussed in more detail in §3.2, where we address datatypes. p ::= Patterns | K Y b: κ c: σ x: τ Data constructor pattern ι ::= Base kinds | ? Star q ::= Term-level names | Constraint Constraint kind | x Term variables | K Data constructors κ, η ::= Kinds | c Coercion variables and axioms | X Kind variables | ι Base kinds w ::= Type-level names | κ1 → κ2 Arrow kinds | a Type variables | ∀ X . κ Kind polymorphism | H Type constants | T κ | F Type functions Promoted type constant Figure 3. Syntax of kinds bnd ::= q: τ | w: κ | X:  Bindings

H ::= Type constants | T Datatypes Γ ::= | Γ, bnd ∅ Contexts | (→) Arrow Figure 1. Syntax of expressions and contexts | (∼) Equality

σ, τ, ϕ, ψ ::= Types System F has three forms of abstraction, over expressions | a Variables C | H Constants λx: τ. e, types Λa: κ. e, and coercions λc: τ. e. Figure 1 shows ↑ | F Type functions that System FC adds a fourth abstraction form, ΛX . e, to abstract ↑ | K Promoted data constructors over kinds. Correspondingly, the term language of FC includes | ∀ a: κ. τ Polymorphic types four forms of application, for expressions, coercions, types, and | ∀ X . τ Kind-polymorphic types kinds. In order to make sure that all subtleties of the semantics are | τ1 τ2 Type application clearly exposed, we choose not to merge the four abstraction forms | τ κ Kind application into one (and similarly applications), as is customary in pure type systems [2]; they are, however, combined in our implementation. γ, δ ::= Coercions | c κ γ Variables and Axioms Γ `tm e : τ | hτi Reflexivity | sym γ Symmetry Γ `tm e : τ1 Γ `co γ : τ1 ∼ τ2 | γ1 γ2 Transitivity T CAST | ∀ a#: κ. γ Type polytype cong Γ `tm e . γ : τ2 | ∀ X . γ Kind polytype cong Γ, X:  `tm e : τ T KABS | γ1 γ2 Type app cong Γ `tm ΛX . e : ∀ X . τ | γ κ Kind app cong Γ `tm e : ∀ X . τ Γ `k κ:  | γ[τ] Type instantiation T KAPP Γ `tm e κ : τ[κ/X ] | γ[κ] Kind instantiation i Γ `tm e : T κ σ | nth γ Nth argument projection for each Figure 4. Syntax of types and coercions Kj : ∀X . ∀a:κj . ∀Yj . ∀bj :ηj . ψj → ϕj → (T X a) ∈ Γ0 0 ηj = ηj [κ/X ] ψ0 = ψ κ/X  [σ/a] ↑ j j Kinds The syntax of kinds of FC is given in Figure 3. In addi- 0   tion to the familiar ? and κ1 → κ2 kinds, the syntax introduces ϕj = ϕj κ/X [σ/a] 0 0 kind variables X and polymorphic kinds ∀ X . κ. The kind well- Γ, Xj : , bj : ηj , cj : ψj , xj : ϕj `tm uj : τ T CASE formedness rules are given in Figure 5. Kinds are uni-typed in the 0 0 0 Γ `tm case e of Kj Yj bj : ηj cj : ψj xj : ϕj → uj : τ sense that there exists only one sort of kinds: . Two more notable ↑ syntactic forms of kinds are included in FC: Figure 2. Typing rules (selected) • Applications of promoted datatypes (§2), of the form T κ. We discuss the details of this mechanism in §3.3. Γ `k κ:  Kind validity Γ `co γ : τ Coercion typing

X:  ∈ Γ c: ∀X . ∀a:η. (τ1 ∼ τ2) ∈ Γ KV VAR for each γ ∈ γ, Γ `k X :  i Γ `co γi : σi ∼ ϕi KV BASE Γ ` σi , ϕi :(ηi [κ/X ]) Γ `k ι:      C VARAX Γ `co c κ γ :(τ1 κ/X [σ/a]) ∼ (τ2 κ/X [ϕ/a]) Γ `k κ1 :  Γ `k κ2 :  KV ARR Γ `k κ1 → κ2 : Γ `ty τ : κ  C REFL Γ ` hτi : τ ∼ τ Γ, X:  `k κ:  co KV ABS Γ `k ∀ X . κ:  Γ `co γ : τ1 ∼ τ2 C SYM Γ `k κ1 :  .. Γ `k κn :  Γ `co sym γ : τ2 ∼ τ1 n ∅ `ty T : ? → ? Γ ` γ : τ ∼ τ Γ ` γ : τ ∼ τ KV LIFT co 1 1 2 co 2 2 3 Γ ` T κ: C TRANS k  Γ `co γ1 γ2 : τ1 ∼ τ3 # Γ `ty τ : κ Kinding Γ, a: κ ` τ1, τ2 : ι Γ, a: κ `co γ : τ1 ∼ τ2 ` Γ w: κ ∈ Γ C TABS K VAR Γ `co ∀ a: κ. γ : ∀ a: κ. τ1 ∼ ∀ a: κ. τ2 Γ `ty w : κ Γ, X:  ` τ1, τ2 : ι ` Γ K: τ ∈ Γ ∅ ` τ κ Γ, X:  `co γ : τ1 ∼ τ2 K LIFT C KABS Γ `ty K : κ Γ `co ∀ X . γ : ∀ X . τ1 ∼ ∀ X . τ2

Γ `k κ:  Γ, a: κ `ty τ : ι Γ ` σ1, σ2 : κ1 → κ2 Γ ` τ1, τ2 : κ1 K ABS Γ ` ∀ a: κ. τ : ι Γ `co γ1 : σ1 ∼ σ2 Γ `co γ2 : τ1 ∼ τ2 ty C APP Γ `co γ1 γ2 : σ1 τ1 ∼ σ2 τ2 Γ `ty τ1 : κ1 → κ2 Γ `ty τ2 : κ1 K APP Γ ` τ , τ : ∀ X . κ0 Γ `ty τ1 τ2 : κ2 1 2 Γ `co γ : τ1 ∼ τ2 Γ `k κ:  Γ, X:  `ty τ : ι C KAPP K KABS Γ `co γ κ : τ1 κ ∼ τ2 κ Γ `ty ∀ X . τ : ι Γ `co γ : ∀ a: κ. τ1 ∼ ∀ a: κ. τ2 Γ `ty σ : κ C TINST Γ `ty τ : ∀ X . κ Γ `k κ1 :  K KAPP Γ `co γ[σ]: τ1[σ/a] ∼ τ2[σ/a] Γ `ty τ κ1 : κ[κ1/X ] Γ `co γ : ∀ X . τ1 ∼ ∀ X . τ2 Γ `k κ:  NST Γ `ty τ1 : ι1 Γ `ty τ2 : ι2 C KI K ARRT Γ `co γ[κ]: τ1[κ/X ] ∼ τ2[κ/X ] Γ ` τ → τ : ι ty 1 2 2 0 Γ `co γ : H κ τ ∼ H κ τ TH ` Γ j 0 C N K EQ Γ `co nth γ : τj ∼ τj Γ `ty (∼): ∀ X . X → X → Constraint Figure 6. Formation rules for coercions Figure 5. Formation rules for kinds and types

↑ actually formed from the application of the constant (∼) to a kind • Another small extension of System FC is a special base kind Constraint [4], which classifies types representing evidence, and two type arguments of that kind. In FC, this equality constraint such as type class dictionaries and type equalities. While in was a special form, but the addition of polymorphic kinds means source Haskell such evidence is implicit, the elaboration of that it can be internalized and treated just like any other type constructor. To keep the syntactic overhead low, we continue to use a source program into F↑ constructs explicit evidence terms C the notation τ ∼ τ to stand for the application (∼) κ τ τ when whose types have kind Constraint. We use the metavariable ι 1 2 1 2 the kind is unimportant or clear from the context. to refer to an arbitrary base kind (? or Constraint). Another minor point is the use of ι to classify the base kinds The kind well-formedness rules in Figure 5 ensure that kinds are in rules K ABS and K KABS (Figure 5). A conventional system first-order, in the sense that we do not include any kind operators. would require that a type ∀a.τ has kind ?, but in our system it In other words, Maybe by itself is not a kind, although Maybe ? is, can have either kind ? or Constraint, because both classify types and there are no classifiers for kind variables other than . inhabited by values. ↑ Types The types of FC are given in Figure 4, and their kind- Coercions Coercions, also shown in Figure 4, are “proofs” that ing rules appear in Figure 5. The new constructs include (i) kind- provide explicit evidence of the equality between types. The judge- polymorphic types ∀X . τ, (ii) kind application τ κ, and (iii) pro- ment Γ `co γ : τ1 ∼ τ2 expresses the fact that the coercion γ is moted data constructors. Kind-polymorphic types classify kind- a proof of equality between the types τ1 and τ2. If such a deriva- abstractions in the expression language. The syntax τ κ applies a tion is possible, it is an invariant of the relation that τ1 and τ2 have type constructor with a polymorphic kind to a kind argument. There the same kind, and that kind instantiates the (∼) constructor in the is no explicit kind abstraction form in the type language for the conclusion of the relation. same reason that there is no explicit type abstraction form—type The coercion forms are best understood by looking at their for- inference in the presence of anonymous abstractions would require mation rules, shown in Figure 6. Coercion variables and uses of higher-order unification. primitive axioms are typed by rule C VARAX. Recall that primi- The syntax of constants includes the equality constructor (∼), tive coercion axioms may be kind-polymorphic since type family which has the polymorphic kind ∀ X . X → X → Constraint by instance declarations in source Haskell may be kind-polymorphic, rule K EQ. This means that equality constraints written τ1 ∼ τ2 are such as the last Shape instance from §2.7. When such axioms are ` Γ Context well-formedness e −→ e0 One-step reduction

GWF EMPTY S BETA ` ∅ (λx: τ. e) e0 −→ e[e0/x] ` Γ X #Γ S CBETA GWF SORT (λc: τ. e) γ −→ e[γ/c] ` Γ, X:  ` ΓΓ `k κ:  a #Γ S TBETA GWF TYVAR (Λa: κ. e) τ −→ e[τ/a] ` Γ, a: κ S KBETA ` ΓΓ `k κ:  F #Γ (ΛX . e) κ −→ e[κ/X ] GWF TYFUN ` Γ, F: κ Ki Y b: η c: ψ x: ϕ → ui ∈ p → u S CASE 0 κ = ∀X .κ → ? case Ki κ σ κ τ γ e of p → u −→ ` ΓΓ `k κ:  T #Γ h i h 0 i GWF TYDATA ui [e/x][γ/c] τ/b κ /Y ` Γ, T: κ

` ΓΓ `ty τ : κ x #Γ 1 2 S PUSH GWF VAR (v . γ) e −→ (v (e . sym (nth γ))) . nth γ ` Γ, x: τ S TPUSH τ = ∀X . ∀a:κ. ∀Y. ∀b:η. (σ → (T X a)) (v . γ) τ −→ v τ . γ[τ] ` ΓΓ `ty τ : ? K #Γ GWF CON S KPUSH ` Γ, K: τ (v . γ) κ −→ v κ . γ[κ] 0 τ = ∀X . ∀a:κ. (τ1 ∼ τ2) ∅ `co γ : T κ σ1 ∼ T κ σ2 ` ΓΓ `ty τ : Constraint c #Γ K: ∀X . ∀a:κ. ∀Y. ∀b:η. (ψ1 ∼ ψ2 → ϕ → (T X a)) ∈ Γ GWF AX 0 h i h 0 i   ` Γ, c: τ ψ1 = ψ1 τ/b κ /Y κ/X 0 h i h 0 i   Figure 7. Context formation rules ψ2 = ψ2 τ/b κ /Y κ/X h i h i ϕ0 = ϕ τ/b κ0/Y κ/X 

for each γj ∈ γ, used, they must be applied to kind and coercion arguments. In this 0 0 0 γj = sym ([a7→nth γ ]ψ1 j ) γj [a7→nth γ ]ψ2 j rule and elsewhere, the notation Γ ` σ, ϕ : κ ensures that both # # for each ej ∈ e, types σ and ϕ have the same kind in the same context. e0 = e . [a7→nth γ0]ϕ0 Coercions include rules for reflexivity, symmetry, transitiv- j j j S CPUSH ity and congruence (rules C TABS through C KAPP). Coercions case (K κ σ κ0 τ γ e) . γ0 of p → u −→ can be destructed, through instantiation (C TINST and C KINST) 1 0 0 0 as well as by appealing to the injectivity of type constructors case K κ σ2 κ τ γ e of p → u (C NTH). Notice that in rule C NTH the kinds of the two appli- S COMB cations are required to be the same—this syntactically ensures that (v . γ1) . γ2 −→ v . (γ1 γ2) # coercions are always between types of exactly the same kind. Figure 8. Operational semantics of F↑ (selected rules) ↑ C Contexts System FC allows top-level definitions for datatypes T, type functions F, and equality axioms c. Rather than give concrete syntax for declarations of these three constants, we instead give In other words, type constants must take all of their kind arguments formation rules for the initial context in which terms are type- before any of their type arguments. This restriction simplifies their checked, shown in Figure 7. The notation x #Γ indicates that x is semantics. For example, because type constants are injective, equa- fresh for Γ. tions between them may be decomposed into equations between their type arguments, as in the rule C NTH. By not allowing kind Operational semantics Selected rules of the operational seman- and type arguments to mix, we can write this rule succinctly. ↑ tics of FC appear in Figure 8, including the β-reduction rules for Data constructors for kind-polymorphic datatypes must them- abstraction forms and for case expressions. The operational seman- selves be kind polymorphic (the alternative would be to allow kind tics includes crucial “push” rules, inherited from FC, which make equalities, just as type equalities are currently allowed for GADTs, sure that coercions do not interfere with evaluation. For example, but we do not: see §3.4). Rule GWF CON requires data construc- rule S PUSH illustrates a situation where a coercion may interfere tors to have types of the following form: with a β-reduction. In that case γ must be a coercion between two (τ → τ ) ∼ (σ → σ ) γ function types, 1 2 1 2 . The rule decomposes K: ∀X . ∀a:κ. ∀Y. ∀b:η. ϕ → (T X a) into two simpler coercions and rewrites the term to expose oppor- tunities for reduction. Data constructors can be polymorphic over kinds and types, all of which must show up as parameters to the datatypes that they 3.2 Kind polymorphism and datatypes construct. Furthermore, data constructors can take additional kind and type arguments that do not appear in the result type, as well We allow datatype definitions to have polymorphic kinds. However, as term arguments whose types may include constraints on any of rule GWF TYDATA requires all type constants to have prenex kind 4 the quantified type variables. As a result, data constructor values quantification, carry six lists of arguments. (Above, ϕ includes both the types of T: ∀X .κ → ?. the coercion and expression arguments.) The treatment of these six arguments shows up in the formation 4 We use the notation ∀X . τ as an abbreviation for ∀X1.∀X2. . . . τ. Like- rule for case expressions (T CASE, from Figure 2), and the two wise, κ → ? abbreviates κ1 → κ2 → ... → ?. reduction rules for case expressions (S CASE and S CPUSH, from Figure 8). These rules were already quite involved in System FC— Θ ` τ κ Type lifting adding kind polymorphism is a straightforward modification. The rule T CASE typechecks a case expression. In this rule, the a 7→ X ∈ Θ L VAR lists κ and σ are the kind and type parameters to some datatype Θ ` a X T . These arguments replace the kind and type variables X and a, Θ ` τ1 κ1 Θ ` τ2 κ2 L ARR wherever they appear in the case expression. The rule then type- Θ ` τ → τ κ → κ checks each branch of the case expression in a context extended 1 2 1 2 Θ, a 7→ X ` τ κ with the “existential” kind and type variables, as well as the coer- L ABS cion assumptions and constructor arguments of that branch. Θ ` ∀ a: ? . τ ∀ X . κ n n The rule S CASE describes the normal reduction of a case ex- ∅ `ty T : ? → ? Θ ` τi κi pression. If the scrutinee is a data constructor value, the appropri- L APP Θ ` T τ T κ ate branch is selected, after substitution of the last four arguments carried by the data constructor value—the “existential” kinds and Figure 9. Type to kind translation types, coercions, and expression arguments. If there is a coercion around the data constructor value, rule S CPUSH pushes the coercion into the arguments of the data con- structor. Again, the complexity of the semantics of this rule is in- 3.4 Design principle: no kind equalities! 0 herited from System FC. The coercion γ and expression arguments There is one other major restriction on promoted types: we do not e are each transformed by types “lifted” to coercions, using the op- promote GADTs. This limitation derives from an important design ↑ eration [a7→nth γ]τ. This operation replaces each type variable ai principle of FC: we do not allow equality constraints between kinds, appearing in the type τ with a coercion nthi γ. We discuss this nor kind coercions. Since GADT data constructors take coercions operation in more detail in §4. For more on the operation of this between types as arguments, their promotions would necessarily rule, we refer readers to previous work [21, 24]. require coercions between kinds. Disallowing kind equality con- straints and coercions means that α-equivalence is the only mean- 3.3 Promotion ingful equivalence for kinds, which dramatically simplifies the sys- We allow the “promotion” of certain type constructors into kinds, tem. The difficulty with kind constraints lies with type equivalence. and the promotion of certain data constructors to become types. But ↑ exactly which type and data constructors are promoted, and what In FC, all nontrivial type conversions must be made explicit in the kinds do the promoted data constructors have? The answers may code, through the use of coercions. Coercions simplify the metathe- be found in Figure 5: ory of the FC language, by rendering type checking trivially decid- able and separating the type soundness proof from the consistency • Type constructors. Rule KV LIFT states that T κ is a valid kind of the type equational theory. In the jargon of dependent type the- n only if T is a fully applied type constructor of kind ? → ?. ory, FC has a trivial definitional equality (types are equal only when α-equivalent) and an expressive propositional equality (coercions • Data constructors. Rule K LIFT states that a data constructor K may be treated as a type if K’s type τ can be promoted are proofs of a much coarser equality between types.) The presence of nontrivial kind equivalences muddies the def- to a kind κ, via the judgement ∅ ` τ κ, whose rules are given in Figure 9. The latter judgement merely replaces inition of propositional equality between types. We would need to type polymorphism in τ with kind polymorphism in κ, and generalize the coercion judgement to mention two types as well as checks that type constructors mentioned in τ can themselves their kinds. But what should the interpretation of this judgement be promoted. be? That the types and their kinds are equal? Should we be able to extract a proof of kind equality from a proof of type equality? Such The rule for promoting type constructors is deliberately restrictive. a system would lead to bloated proofs and many additional rules. There are many Haskell type constructors that do not have kinds of We plan to address these issues in future work, using ideas the form ?n → ?. from Observational Type Theory [1]. But even if we are able to design a sensible core language, there is still the issue of the • We do not promote higher-order types of kind, say, (? → ?) → complexity of the source language. Heterogeneous equality in Coq ?. If we did so, we would need a richer classification of kinds and Agda is notoriously difficult for users to understand and use. By to ensure that such promoted higher-order types were applied only allowing one-level indexing, we have defined a simple, well- to appropriate arguments. behaved language for users to get started with dependently-typed • We do not promote a type whose kind itself involves promoted programming. types, such as Vec : ? → Nat → ?. If we did so, the second argument to Vec would have to be a kind classified by Nat. In order to make this possible we would either have to allow 4. Metatheory “double promotion” (such as promoting a natural number value ↑ We now turn to the formal properties of FC, and ultimately show all the way to the kind level), or make the type and kind levels that the system enjoys subject reduction and progress under some fully dependent. Either approach would complicate matters, so consistency requirements about the initial environment. We begin we rule them out for now. with some scaffolding lemmas. • We do not promote type constructors with polymorphic kinds. Lemma 4.1 . If we did, we would need a system of polymorphic sorts to (Kind substitution) accommodate the promoted kinds. At present, this seems like 1. If ` Γ1, (X: ), Γ2 and Γ1 `k κ:  then ` Γ1, Γ2[κ/X ]. extra complication for little benefit. 0 0 2. If Γ1, (X: ), Γ2 `ty τ : κ and Γ1 `k κ :  then Γ1, Γ2[κ /X ] `ty 0 The guiding principle here is that kinds are not classified, or, to τ[κ /X ]: κ. 3. If Γ , (X: ), Γ ` η : and Γ ` κ: then Γ , Γ [κ/X ] ` put it another way, there is only one sort  in the kind validity 1  2 k  1 k  1 2 k η[κ/X ]: . judgement Γ `k κ: .  The lemmas above are proved simultaneously by induction on Lemma 4.9 (Lifting). If Γ, (a: κ) `ty τ : η and Γ `co γ : τ1 ∼ the height of the derivation. In each case, the induction hypothesis τ2 with Γ ` τ1, τ2 : κ then Γ `co [a7→γ]τ : τ[τ1/a] ∼ τ[τ2/a]. asserts that all lemmas hold for derivations of smaller height. They With all the scaffolding in place, we can show that evaluation have to be proved simultaneously, as each of the three judgements preserves types. appeals to the other two. Theorem 4.10 (Subject reduction). Let Γ0 be the initial environ- Lemma 4.2 (Type substitution). ment that does not contain any term, type, or kind variable bindings

1. If ` Γ1, (a: η), Γ2 and Γ1 `ty τ : η then ` Γ1, Γ2[τ/a]. (we will refer to this as the top-level environment in what follows). The following is true: if Γ0 `tm e1 : τ and e1 −→ e2 then 2. If Γ1, (a: η), Γ2 `ty σ : κ and Γ1 `ty τ : η then Γ1, Γ2[τ/a] `ty σ[τ/a]: κ. Γ0 `tm e2 : τ. 3. If Γ1, (a: η), Γ2 `k κ:  and Γ1 `ty τ : η then Γ1, Γ2[τ/a] `k 4.2 Progress κ: .  Despite the tedious scaffolding lemmas, subject reduction is not With these two lemmas, we can prove that the derived coercions hard conceptually, since each evaluation rule constructs proof terms of our system are homogeneous. which justify the typeability of the reduct. Progress, on the other hand, is more interesting: the formal op- Lemma 4.3 (Coercion homogeneity). If Γ ` γ : τ ∼ τ then co 1 2 erational semantics of F↑ involves coercions and pushing coercions Γ ` τ , τ : κ for some kind κ. C 1 2 in terms, and we must make sure that these coercions do not stand in As a corollary, if Γ `co γ : τ1 ∼ τ2 then Γ `ty τ1 ∼ the way of ordinary β-reductions. If coercions could prevent some τ2 : Constraint, since the two types have the same kind and the reductions, that would contradict our claim that coercions can be application of the (∼) constructor is possible by rule K EQ. safely and entirely erased at runtime. The coercion erasure of a ↑ Moreover, coercion derivations (like type and kind derivations) “safe” stuck FC term could lead to a crashing program. are preserved by type and kind substitution. Intuitively, for progress to hold we must impose the restriction that if a coercion coerces the type of a value to another value type Lemma 4.4 (Kind substitution in coercions). If Γ , (X: ), Γ ` 1  2 co then the two value types have the same runtime representation. We γ : τ ∼ τ and Γ ` κ: then Γ , Γ [κ/X ] ` γ[κ/X ]: 1 2 1 k  1 2 co formalize this condition below. τ1[κ/X ] ∼ τ2[κ/X ]. Definition 4.11 (Value type). A type τ is a value type in an envi- Lemma 4.5 (Type substitution in coercions). If Γ1, (a: η), Γ2 `co ronment Γ if Γ `ty τ : ι and moreover it is of the form H κ σ, or γ : τ1 ∼ τ2 and Γ1 `ty τ : η then Γ1, Γ2[τ/a] `co γ[τ/a]: ∀ a: κ. σ, or ∀ X . σ. τ1[τ/a] ∼ τ2[τ/a]. To prevent unsound type equalities, the initial environment con- We may also substitute coercions inside other coercions: taining axioms and datatypes (but no term, type, and coercion vari- Lemma 4.6 (Coercion substitution in coercion). ables) should be consistent, a notion that we directly adapt from previous work on FC [21, 24]. 0 1. If Γ1, (c: τ1 ∼ τ2), Γ2 `co γ : σ1 ∼ σ2 and Γ1 `co γ : τ1 ∼ 0 0 Definition 4.12 (Consistency). A context Γ is consistent iff τ2 then Γ1, Γ2[γ /c] `co γ[γ /c]: σ1 ∼ σ2. 0 2. If ` Γ1, (c: τ1 ∼ τ2), Γ2 and Γ1 `co γ : τ1 ∼ τ2 then • If Γ `co γ : H κ σ ∼ τ, and τ is a value type, then τ = H κ ϕ. 0 ` Γ1, Γ2[γ /c]. • If Γ `co γ :(∀ a: κ. σ) ∼ τ, and τ is a value type, then 0 3. If Γ1, (c: τ1 ∼ τ2), Γ2 `ty τ : κ and Γ1 `co γ : τ1 ∼ τ2 then τ = ∀ a: κ. ϕ. 0 Γ1, Γ2[γ /c] `ty τ : κ. • If Γ `co γ :(∀ X . σ) ∼ τ, and τ is a value type, then 0 4. If Γ1, (c: τ1 ∼ τ2), Γ2 `k κ:  and Γ1, Γ2[γ /c] `k κ: . τ = ∀ X . ϕ. Under the assumption of consistency for the top-level defini- 4.1 Subject reduction tions we can state and prove progress. To show subject reduction, we first prove a substitution theorem for Theorem 4.13 (Progress). If Γ ` e : τ and Γ is a top-level terms. 0 tm 0 consistent environment, then either e is a value possibly wrapped Theorem 4.7 (Substitution). in casts, or e −→ e0 for some e0.

1. If Γ1, (X: ), Γ2 `tm e : τ and Γ1 `k κ:  then As defined above, consistency is a property not only of the Γ1, Γ2[κ/X ] `tm e[κ/X ]: τ[κ/X ]. initial environment but also of the coercion formation judgement. It 2. If Γ1, (a: η), Γ2 `tm e : τ and Γ1 `ty σ : η then is therefore not easy to check. For this reason, previous work [24] Γ1, Γ2[σ/a] `tm e[σ/a]: τ[σ/a]. gives sufficient conditions exclusively on the top-level environment which guarantee consistency (for a similar but simpler coercion 3. If Γ1, (c: σ1 ∼ σ2), Γ2 `tm e : τ and Γ1 `co γ : σ1 ∼ σ2 language). These conditions can be adapted in a straightforward then Γ1, Γ2[γ/c] `tm e[γ/c]: τ. way here—the only interesting bit is that type family axioms now We also need a substitution lemma for terms: involve type and kind arguments and both forms of arguments have to be taken into account when checking the overlap of these axioms. Lemma 4.8 (Expression substitution). If Γ , (x: σ), Γ ` e : τ 1 2 tm We finally remark that unsound equalities such as Int∼Bool and Γ ` u : σ then Γ , Γ ` e[u/x]: τ. 1 tm 1 2 tm can be derived by terms, since Int∼Bool is just a type, so one Finally, the crux of subject reduction is the so-called lifting may wonder how type safety can possibly hold. For instance, the ↑ lemma, which also has the same key role for proving subject reduc- following is valid FC code: tion in previous work on F . Recall the lifting operation from §3.2, C f :: (Int∼Bool) denoted [a7→γ]τ. Lifting provides a way to convert a type to a coer- f = ⊥ cion by substituting its free type variables with coercions between types of the appropriate kind. Its formal definition is straightfor- Fortunately, such terms do not pose any danger to type safety, ward. because the syntax prevents us from injecting ordinary terms into the universe of coercions and using them to erroneously cast the are less fully tested. The changes required to the compiler, although types of other expressions. widespread, were surprisingly modest. We briefly summarize them here. To understand this explanation it may help to recall that 5. Surface language and elaboration GHC’s type inference engine elaborates input terms by filling in implicit type and kind abstractions and applications, so that the When extending Haskell’s surface syntax to expose the new fea- ↑ terms can subsequently be desugared into F . tures to users, we found two major issues that needed to be ad- C dressed. First, a new mechanism for namespace resolution was 6.1 The Core language needed; second, we had to decide when and to what degree kind generalization should be performed. We extended GHC’s Core language to support kind polymorphism, which was mostly straightforward. The only awkward point was 5.1 Namespace resolution that we often abstract a term over a set of type variables whose order used to be immaterial. Now we need to abstract over type As we saw in §2.2, type and data constructors have separate names- and kind variables, and we must ensure that the kind variables come paces, so that type constructors and data constructors can be given first, because they may appear in the kinds of the type variables. the same name. This practice is common in Haskell programs, start- ing with Prelude declarations such as (, ) and []. However, when 6.2 Type inference for terms data constructors can appear in types, it is no longer clear what namespace should be used to resolve type constants. SHE explored The type inference engine needed to be extended in two main ways: one solution to this problem: it requires all promoted data construc- • When instantiating the type of a kind-polymorphic function, say tors and kinds to be surrounded by curly braces. f :: ∀ X . ∀ a: X . τ we must instantiate a fresh unification kind X a data Vec :: ? → {Nat} → ? where variable for , and a fresh unification type variable for , with an appropriate kind. VNil :: Vec a {Z} VCons :: a → Vec a {n } → Vec a {S n } • When unifying a type variable with a type we must unify its kind with the kind of the type. Note that curly braces can surround type expressions, not just data constructors, as in {S n } above. Such notation is useful for All kind unifications are solved on the fly by the usual side- expressions with multiply lifted components. However, in SHE, effecting unification algorithm, and do not generate evidence. In type variables with promoted kinds must also be lifted, meaning contrast, type equalities are often gathered as constraints to be that SHE is based on lifting values rather than just constructors. solved later, and are witnessed by evidence in the form of coercion In contrast, we separate the issue of namespace resolution from terms [18]. Not having to do this step for kind equalities rests on that of semantics by adopting the single quote mechanism. Each the design principle discussed in §3.4. (ambiguous) data constructor must be marked. We find that this notation is easier to specify, as the treatment of type variables is 6.3 Kind inference for types completely standard. Furthermore, because quotes can often be GHC allows the user to write type signatures, such as omitted, it is also visually lighter. f :: Typeable a ⇒ [Proxy a ] → TypeRep 5.2 Kind generalization When elaborating this type signature, the inference engine per- ↑ The second issue that we needed to address in the source language forms kind generalization of the type, yielding the FC type extension was the specification of where kind generalization should f : ∀X . ∀a : X . Typeable X a → [Proxy X a ] → TypeRep occur, and how it should interact with type and kind annotations and lexically scoped type (and now kind) variables. Type declarations themselves are a little more complex. Consider In general, our extension tries to be consistent with the existing data T f a = MkT (S a f ) | TL (f a) treatment of type polymorphism. Kind variables are generalized data S b g = MkS (T g b) at the same places that type variables are, and kind variables can be brought into scope using annotations just like type variables. These two mutually recursive definitions must be kind checked Furthermore, kind variables that appear in type argument signatures together. Indeed the situation is precisely analogous to the well- are quantified. For example, the declaration of T below is valid, and studied problem of type inference for mutually recursive term def- the kind of T is inferred to be ∀X . (X → ?) → X → ?. initions. data T (a :: X → ?) b = K (a b) • We sort the type declarations into strongly connected compo- nents, and kind check them in dependency order. In the same way, kind polymorphism can be explicitly specified in class declarations. For example, the definition of IFunctor in • A type constructor can be used only monomorphically within §2.6 is accepted just as it appears there, but the programmer may its mutually recursive group. also write this more explicit definition: • Type inference assigns each type constructor a kind variable, then walks over the definitions, generating and solving kind class IFunctor (f :: (X1 → ?) → X2 → ?) where constraints. imap :: ∀(s, t :: X1 → ?). (s :→ t) → (f s :→ f t) • Finally, the kind of each constructor in the group is generalized. The first line brings the kind variables X1 and X2 into scope, along with the type variable f ; all are then mentioned in the type of imap. Class declarations are handled in the same way. There is nothing new here—it all works in precisely the same way as for terms—so 6. Implementation we omit a precise account. We have a preliminary implementation of our system ready for release with GHC version 7.4. This initial release includes a solid 7. Related work ↑ implementation of FC in the Core language; the source-language Promoting data types The starting point for this work has been features, and the kind inference that supports them, mostly work but Conor McBride’s Strathclyde Haskell Enhancement preproces- sor [11], which, among other features, allows users to promote Kind polymorphism has also been added to typed intermediate datatypes to be used in the type language. Throughout this paper, languages, even when the source language did not include kind we have already discussed several points of both similarity and polymorphism. Trifonov et al. [23] found that the addition of kind difference between our work and SHE. polymorphism to the FLINT/ML compiler allowed it to be fully The language extensions we describe here are most closely reflexive. In other words, they were able to extend their type- related to the LX calculus [? ] and the Ωmega language [19, 20]. analyzing operations so that they are applicable to the type of any These languages also allow the definition of datakinds and type- runtime value in the language. level functions over datatypes. Like both of these systems, GHC More formally, the properties of a polymorphic lambda calculus maintains a phase separation: terms can depend on types, but not with kind polymorphism were studied by Girard [5]. Girard showed ↑ vice versa. In terms of expressiveness, FC is very similar to LX: that this calculus can encode a variant of the Burali-Forti paradox ↑ and is thus inappropriate for use as a constructive logic. This proof FC includes kind polymorphism and does not require termination proofs for type-level computation, but lacks anonymous functions of “Girard’s paradox” is described in detail by Barendregt [2]. ↑ Hurkens later simplified this paradox [8]. It is not clear whether this at the type-level. However, FC is less expressive than Ωmega, which allows GADT programming at the type level. Because datakinds result holds for our system, since we do not directly allow function may be indexed, Ωmega also allows the definition of datatypes in abstraction at the type or kind levels. However, even if it did, it the kind language. In fact, Ωmega has a hierarchy of levels (types, would result in a non-normalizing term, which Haskell already has kinds, superkinds, etc.) where each level includes datatypes that can in abundance. be indexed by elements from any of the higher levels. All datatype Kind-indexed type families The idea of allowing type functions definitions are “level-polymorphic”, meaning that they can be used to dispatch on kinds is a fairly novel component of this extension. at any level. On the other hand, Ωmega does not compile to an Work by Morrisett et al. on intensional type analysis [6] included explicitly typed language with evidence terms, and LX does not an operator that dispatched on types, but not kinds. Indeed, the include explicit evidence for type equalities. This makes it harder extension of that work by Trifonov et al. [23] relied on the fact in those languages to reason about type safety, robustness under that kind polymorphism was parametric. In System FC, we have transformations, and the soundness of type and kind inference. In separated the soundness of the language from the decidability of ↑ FC many of the restrictions in our design (such as no evidence- type checking [24]. based kind-equalities) are motivated by our desire to keep the Although we do not yet have many motivating examples of explicitly typed intermediate language simple. the use of kind-indexed type families, we still believe that they Our extension does not add full-spectrum dependency to Haskell. have great potential. For example, we can use them to define the It merely makes the phase distinction less distinct. Full-spectrum ↑ polykinded-types [7] found in Generic Haskell. System FC cannot languages actually allow program expressions to invade the type yet write the polytypic functions that inhabit such types, but we system, but in the presence of nontermination or other effects, of- plan to further extend the language with such support (see §??). ten limit those expressions to a subset with a trivial definition of equality. For example, the Aura programming language [? ] and Why not full-spectrum dependent types? One might well ask: Licata and Harper’s Positive Dependent Types [? ] only allow de- why not just use, say, Agda [14] or Coq [3]? What benefit is there in pendency on values of positive types (such as datatypes formed adding dependent type features to Haskell without going all the way from strict products and sums). Alternatively, the F∗ language [? ] to a full-spectrum dependently typed language? We see a number allows dependency for all values, but mandates a trivial definition of advantages of our evolutionary approach: of equality for functions. In contrast, promotion is more similar to • Type inference. Haskell enjoys robust type inference, even for the duplication described above. Although Haskell datatypes are top-level terms. Adding full dependent types would severely nonstrict, we do not promote values, only the constructors used to complicate matters at best. Promotion, however, requires only form them. slight enhancements to existing inference mechanisms (§5). Agda takes the promotion idea one step further with its experi- • Phase distinction. Since Haskell types never actually depend mental support for universe polymorphism5. In Agda, all definitions on terms and vice versa, types can be safely erased before (including datatypes) may be polymorphic over their level. Func- runtime. This is still true for promoted values. Erasure analysis tions that work with such datatypes may also be level polymorphic, for full-spectrum languages, on the other hand, is an active area so the same function can be used both at runtime and for type-level of research. computation. This extension is much more sophisticated than the simple form of promotion that we describe here, and its interac- • Explicit, strongly typed evidence. This allows us to be cer- tions with the rest of the type system are not yet well understood. tain that type and kind inference is sound and does not accept programs that may crash at runtime. Kind polymorphism Kind polymorphism is an often requested feature for Haskell in its own right. In fact, the Utrecht Haskell • Simple type checking. System FC, into which GHC desugars Compiler (UHC)6 [? ] already supports kind polymorphism. In Haskell source code, enjoys a simple, linear-time type checking particular, unknown kinds of type constructors do not default to algorithm, guided by the presence of explicit coercion terms. kind ?, but are instead generalized. The particular motivation for This remains true for our promoted variant. Type checking the this addition to UHC seems to be the ability to define a kind- core language for a full-spectrum dependently typed intermedi- polymorphic Leibniz equality datatype: ate language, on the other hand, must take into account a more complex equivalence on types which includes β-equivalence. data Eq a b = Eq (∀f . f a → f b) • Optimization. Thanks again to the presence of explicit coer- However, UHC does not compile to a typed intermediate language cion terms, Haskell’s core language can be aggressively opti- ↑ mized while remaining statically typed. like System FC. • Familiarity. Many programmers are already familiar with 5 http://wiki.portal.chalmers.se/agda/agda.php?n=Main. Haskell, and it enjoys a large collection of existing libraries UniversePolymorphism and tools. Modest, backwards-compatible extensions will be 6 http://www.cs.uu.nl/wiki/UHC adopted in ways that major, breaking changes cannot. 8. Future work kinds, we now have type families which are “naturally” closed. For ↑ example, given a type family Foo with two clauses, Foo Zero = ... One of the main driving forces behind the current design of FC has been simplicity. We wanted to make sure we have all the details and Foo (Succ n) = ..., it is impossible to add any more well- solidly in place before introducing any complications. There are, kinded clauses without overlap. Could the compiler somehow take however, several further directions we would like to explore. advantage of this knowledge? 8.1 Promoting functions 8.5 Kind-indexed GADTs If we can promote (some) term-level data constructors to type Recall the definition of the Typeable class with a kind-polymorphic constructors, why not promote (some) term-level functions to type- type from §2.5: level functions? We have not done so yet because we already have class Typeable a where a carefully-limited way to define type-level functions, and it seems typeOf :: Proxy a → TypeRep awkward to specify exactly which term-level functions should be promoted. However, the resulting code duplication is irksome, and Notice that typeOf ’s return type, TypeRep, does not mention a. It there is no difficulty in principle, so we expect to revisit this topic would be nicer to have typed TypeReps by using GADTs whose in the light of experience. constructors refine types and kinds, for instance: 8.2 Generating singleton types data TypeRep (a :: κ) where RepInt :: TypeRep Int When using promoted types as indices, one quickly comes to desire RepList :: (∀a. TypeRep a → TypeRep [a ]) → TypeRep [] the ability to do case analysis on them. Of course, this is not directly possible, since types are erased before runtime. A well-known tech- ↑ Notice that RepInt must have type TypeRep ? Int in FC whereas nique for overcoming this limitation is the use of singleton types, RepList returns TypeRep (? → ?)[]. However, supporting kind- which allow doing case analysis on value-level representatives in indexed GADTs is tricky. Just as GADTs currently introduce type lieu of types (the survey article on programming in Ωmega [20] of- equalities, these kind-indexed GADTs could introduce kind equal- fers an excellent introduction to this technique). We would like to ities, an avenue that we are not prepared to take (see the discussion avoid this additional source of code duplication by automatically in §3.4). generating singleton types from datatype declarations. On the other hand, there may be just enough machinery already For instance, consider implementing a function replicate, ↑ in FC to deal with kind-indexed GADTs in a more primitive fashion which creates a Vec of a certain (statically determined) length. than ordinary GADTs, by employing unifiers for kind equalities, This can be accomplished with the help of singleton type SNat: in the same way that GADTs used to be implemented in pre-FC data SNat :: Nat → ? where times [15]. We plan to explore this direction in the future. SZero :: SNat Zero SSucc :: SNat n → SNat (Succ n) Acknowledgments replicate :: SNat n → a → Vec a n Thanks to Conor McBride, Tim Sheard, the members of the Penn replicate SZero = VNil PL Club, and the anonymous reviewers for many helpful and inspir- replicate (SSucc n) x = VCons x (replicate n x) ing suggestions. This work was partially supported by the National Automatically generating singleton types is not hard [11, 13]. More Science Foundation (NSF grants 0910500 and 1116620). interesting would be the design of convenient surface syntax to completely hide their use, so users could just “pattern match on References types”. [1] T. Altenkirch, C. McBride, and W. Swierstra. Observational equality, now! In Proceedings of the 2007 workshop on Programming 8.3 Promoting primitive datatypes languages meets program verification, PLPV ’07, pages 57–68, New Another useful extension would be the promotion of primitive York, NY, USA, 2007. ACM. built-in datatypes, such as integers, characters, and strings. For [2] H. P. Barendregt. Lambda calculi with types, pages 117–309. Oxford instance, having promoted string literals as types would allow us to University Press, Inc., New York, NY, USA, 1992. go well beyond heterogeneous type-level lists to type-level records, [3] Y. Bertot and P. Casteran.´ Interactive Theorem Proving and Program as in the Ur programming language [? ]. Development. Coq’Art: The Calculus of Inductive Constructions. There are no implementation or theoretical difficulties with sim- Texts in Theoretical . Springer-Verlag, 1 edition, ply promoting primitive datatypes. However, our implementation June 2004. does not yet promote primitive datatypes because, to make this fea- [4] M. Bolingbroke. Constraint Kinds for GHC. 2011. URL ture truly usable, we would also like to promote primitive oper- http://blog.omega-prime.co.uk/?p=127. ations on these datatypes (such as integer addition or string con- [] A. Chlipala. Ur: statically-typed metaprogramming with type-level catenation) and integrate these operations with type inference and record computation. SIGPLAN Not., 45:122–133, June 2010. ISSN evidence generation. Some promising work in this direction is Di- 0362-1340. 7 atchki’s recent addition of type-level naturals to GHC. [] K. Crary and S. Weirich. Flexible type analysis. In Proceedings of the fourth ACM SIGPLAN International Conference on Functional 8.4 Closed type families Programming (ICFP ’99), pages 233–248, 1999. Type families, as currently implemented in GHC, are open [17]: it [] A. Dijkstra, J. Fokker, and S. D. Swierstra. The architecture of the is always possible to add more clauses to a type family definition at Utrecht Haskell compiler. In Proceedings of the 2nd ACM SIGPLAN any time, as long as they do not overlap with existing clauses. In a symposium on Haskell, Haskell ’09, pages 93–104, New York, NY, world where the only base kind is ?, this makes sense, since ? itself USA, 2009. ACM. is open. However, with the ability to promote closed datatypes to [5] J.-Y. Girard. Interpretation´ fonctionelle et elimination´ des coupures de l’arithmetique´ d’ordre superieur´ . PhD thesis, Universite´ Paris VII, 7 http://hackage.haskell.org/trac/ghc/wiki/TypeNats 1972. [6] R. Harper and G. Morrisett. Compiling polymorphism using [21] M. Sulzmann, M. M. T. Chakravarty, S. Peyton Jones, and K. Don- intensional type analysis. In Proceedings of the 22nd ACM SIGPLAN- nelly. System F with type equality coercions. In Proceedings of the SIGACT symposium on Principles of programming languages, POPL 2007 ACM SIGPLAN international workshop on Types in languages ’95, pages 130–141, New York, NY, USA, 1995. ACM. design and implementation, TLDI ’07, pages 53–66. ACM Press, [7] R. Hinze. Polytypic Values Possess Polykinded Types. In Proceedings 2007. of the 5th International Conference on Mathematics of Program [] N. Swamy, J. Chen, C. Fournet, P.-Y.Strub, K. Bhargavan, and J. Yang. Construction, MPC ’00, pages 2–27, London, UK, 2000. Springer- Secure distributed programming with value-dependent types. pages Verlag. 266–278. [8] A. J. C. Hurkens. A Simplification of Girard’s Paradox. In [22] The Coq Team. Coq. URL http://coq.inria.fr. Proceedings of the Second International Conference on Typed [23] V. Trifonov, B. Saha, and Z. Shao. Fully Reflexive Intensional Type Lambda Calculi and Applications, pages 266–278, London, UK, Analysis. In Fifth ACM SIGPLAN International Conference on 1995. Springer-Verlag. , pages 82–93. ACM Press, 2000. [] L. Jia, J. A. Vaughan, K. Mazurak, J. Zhao, L. Zarko, J. Schorr, [24] S. Weirich, D. Vytiniotis, S. Peyton Jones, and S. Zdancewic. Gener- and S. Zdancewic. Aura: a programming language for authorization ative type abstraction and type-level computation. In Proceedings of and audit. In Proceeding of the 13th ACM SIGPLAN international the 38th annual ACM SIGPLAN-SIGACT Symposium on Principles of conference on Functional programming, ICFP 2008, Victoria, BC, programming languages, POPL ’11, pages 227–240, New York, NY, Canada, September 20-28, 2008, pages 27–38, 2008. USA, 2011. ACM. [9] O. Kiselyov, R. Lammel,¨ and K. Schupke. Strongly typed heteroge- neous collections. In Haskell 04: Proceedings of the ACM SIGPLAN workshop on Haskell, pages 96–107. ACM Press, 2004. []R.L ammel¨ and S. Peyton Jones. Scrap your boilerplate: a practical design pattern for generic programming. In Proceedings of the 2003 ACM SIGPLAN international workshop on Types in languages design and implementation, TLDI ’03, pages 26–37, New York, NY, USA, 2003. ACM. [] R. Lammel¨ and S. Peyton Jones. Scrap your boilerplate with class: extensible generic functions. In Proceedings of the tenth ACM SIGPLAN international conference on Functional programming, ICFP ’05, pages 204–215, New York, NY, USA, 2005. ACM. [] D. Licata and R. Harper. Canonicity for 2-dimensional type theory. In POPL ’12. To appear. [] D. R. Licata and R. Harper. Positively dependent types. In PLPV ’09: Proceedings of the 3rd Workshop on Programming Languages Meets Program Verification, pages 3–14, New York, NY, USA, 2009. ACM. ISBN 978-1-60558-330-3. [10] S. Marlow, editor. Haskell 2010 Language Report. 2010. URL http://www.haskell.org/onlinereport/haskell2010/. [11] C. McBride. The Strathclyde Haskell Enhancement. URL http: //personal.cis.strath.ac.uk/~conor/pub/she/. [12] C. McBride. Kleisli arrows of outrageous fortune. 2011. URL http://personal.cis.strath.ac.uk/~conor/Kleisli.pdf. [] N. P. Mendler. Predicative type universes and primitive recursion. In Proceedings, Sixth Annual IEEE Symposium on Logic in Computer Science, 15-18 July, 1991, Amsterdam, The Netherlands, pages 173– 184. IEEE Computer Society, 1991. [13] S. Monnier and D. Haguenauer. Singleton types here Singleton types there Singleton Types Everywhere, 2009. [14] U. Norell. Towards a practical programming language based on dependent type theory. Chalmers University of Technology, 2007. [15] S. Peyton Jones, D. Vytiniotis, S. Weirich, and G. Washburn. Simple unification-based type inference for GADTs. In Proceedings of the eleventh ACM SIGPLAN international conference on Functional programming, ICFP ’06, pages 50–61. ACM Press, 2006. [17] T. Schrijvers, S. Peyton Jones, M. M. T. Chakravarty, and M. Sulz- mann. Type checking with open type functions. In Proceeding of the 13th ACM SIGPLAN international conference on Functional programming, ICFP ’08, pages 51–62. ACM Press, 2008. [18] T. Schrijvers, S. Peyton Jones, M. Sulzmann, and D. Vytiniotis. Complete and decidable type inference for GADTs. In Proceedings of the 14th ACM SIGPLAN international conference on Functional programming, ICFP ’09, pages 341–352, New York, NY, USA, 2009. ACM. [19] T. Sheard. Type-level Computation Using Narrowing in Omega. Electr. Notes Theor. Comput. Sci., 174(7):105–128, 2007. [20] T. Sheard and N. Linger. Programming in Omega. In CEFP, pages 158–227, 2007.