Generative Type Abstraction and Type-level Computation (Extended Version) Stephanie Weirich Dimitrios Vytiniotis

Generative Type Abstraction and Type-level Computation (Extended Version) Stephanie Weirich Dimitrios Vytiniotis
Generative Type Abstraction and
Type-level Computation (Extended Version)
Dimitrios Vytiniotis
Simon Peyton Jones
Stephanie Weirich
University of Pennsylvania
Philadelphia, PA, USA
[email protected]
Microsoft Research
Cambridge, UK
Modular languages support generative type abstraction, ensuring
that an abstract type is distinct from its representation, except inside
the implementation where the two are synonymous. We show that
this well-established feature is in tension with the non-parametric
features of newer type systems, such as indexed type families and
GADTs. In this paper we solve the problem by using kinds to
distinguish between parametric and non-parametric contexts. The
result is directly applicable to Haskell, which is rapidly developing
support for type-level computation, but the same issues should arise
whenever generativity and non-parametric features are combined.
Categories and Subject Descriptors D.3.3 [Language Constructs
and Features]: Abstract data types; F.3.3 [Studies of Program
Constructs]: Type structure
General Terms Design,Languages
University of Pennsylvania
Philadelphia, PA, USA
[email protected]
concrete representation type C as synonymous – we call this
flexible type generativity.
• However, given type-level function F the result of (F A) and
(F C) may differ, so A and C cannot be synonymous.
Resolving this conflict is the subject of this paper. Specifically our
contributions are:
• We show in Section 2 that the naive combination of type genera-
tivity and non-parametric type-level features can violate soundness; a problem that already manifests in the Glasgow Haskell
Compiler, and affects not only type-level non-parametric functions, but also other forms of non-parametric constructs, such as
generalized algebraic datatypes (GADTs) [Cheney and Hinze
2003; Hinze et al. 2002; Peyton Jones et al. 2006; Xi et al.
• We formalize a solution to this problem that reconciles flexi-
Keywords Haskell, Newtype deriving, Type functions
Steve Zdancewic
Generative type abstraction allows programmers to introduce new
type constants in their programs that are isomorphic to existing
types; examples include ML’s module system [Milner et al. 1997;
Pierce 2005, Ch. 8], and Haskell’s newtype construct [Peyton
Jones et al. 2003]. Type generativity is important because it supports modularity by enforcing abstraction: the implementor of a
module can move freely between the abstract and representation
types, whereas to the client of the module the two types are completely distinct.
There is growing interest in languages that support some form of
type-level computation including Haskell’s type classes [Hall et al.
1996] and indexed type families [Kiselyov et al. 2010]. However,
there is a fundamental tension between type-level computation and
generative type abstraction, at least in the latter’s more flexible
forms. To summarize very briefly, the conflict is this:
ble type generativity and non-parametric type functions in Section 3. Our language, FC2 , builds on GHC’s existing core
language, System FC [Sulzmann et al. 2007], which supports
erasable type-level coercions. The key ingredient in our solution
is to employ kinds decorated with roles to mark the distinctions
that type contexts may make.
• We prove that FC2 programs are type safe, provided that user
axioms and definitions give rise to consistent axiom sets (Section 4).
• We give sufficient conditions for showing the consistency of
an axiom set. For our proofs, we introduce a rewrite system for types that is novel in two dimensions: rewriting (i) is
role-sensitive, and (ii) need not be strongly normalizing (Section 4.2).
• We present a Haskell-specific result: we show how Haskell
source programs that may involve non-parametric type contexts
and flexible type generativity can be translated to yield provably
consistent FC2 axiom sets. (Section 5)
• To maximize re-use and convenience, it is very desirable for
• Our core language is an improvement of System FC since it
the implementor to be able to treat the abstract type A and its
permits safe flexible type generativity, but also unsaturated type
functions. Perhaps surprisingly, our language is additionally
a significant simplification of the original System FC, which
removes several of the original coercion constructs that we have
identified to be encodable (even in the original System FC). We
discuss these differences in Section 6.2.
For the sake of concreteness we build our presentation around
Haskell and System FC, since this setting allows us to demonstrate our points with real code, instead of using a hypothetical
λ-calculus. However, we stress that our work is applicable when-
[Copyright notice will appear here once ’preprint’ option is removed.]
Coercion lifting: if for two types ϕ and ψ we have ϕ ∼ ψ
(for example, if they are the abstract and concrete types of
a newtype declaration), then T ϕ ∼ T ψ for any type
constructor T .
ever flexible type generativity and non-parametric type-level features are combined. For example, the very same issues could arise
in extensions of the ML module system. We discuss related work
in Section 7.
The problems with generative type abstraction
Generative type abstraction is an extremely useful mechanism for
enforcing abstraction barriers and for refining interfaces. To see this
benefit, let us consider the Haskell incarnation of type generativity,
namely newtype definitions. In Haskell, the programmer may
declare a newtype Age, with concrete representation type Int,
newtype Age = MkAge Int
The implementor uses the “data constructor” MkAge to coerce an
Int to an Age, and pattern matching to effect the inverse coercion.
For example:
addAge :: Age → Int → Age
addAge (MkAge a) n = MkAge (a+n)
The client, in contrast, can be prevented from making such conversions, by using the module system to hide the MkAge constructor:
module AgeModule( Age, addAge, ... ) where
-- Age definition and implementation
Coercion lifting
In describing MkAge, we wrote “data constructor” in quotes because although it behaves in many ways like a data constructor, its
cost model is different. Specifically, a newtype definition guarantees that the abstract type really is represented by the concrete type,
so the runtime conversion cost is zero. That would not be true if
Age were instead declared with data instead of newtype.
So Age can be coerced to an Int, and vice versa, for free—i.e.
without runtime cost—because it is an Int. Notationally, we say
that Int ∼ Age, where we use ∼ for type equality1 . But what
about, say, Maybe Age?
data Maybe a = Nothing | Just a
Obviously, Maybe Age should be freely coercible to Maybe Int,
because the two are represented identically. Alas, in Haskell 98 one
would have to write
cvtAge :: Maybe Age → Maybe Int
cvtAge t = mapMaybe (λ(MkAge a) → a) t
-- mapMaybe :: (a → b) → Maybe a → Maybe b
This is unsavory for several reasons: (a) it is tedious for the programmer; (b) it may be difficult to implement the necessary “map”
function; (c) it is hard for the compiler to eliminate a runtime call
to the map function (let alone to guarantee to do so) especially if
the map function is recursive. As an example of (b) consider the
mapping function for the type T shown below, with co- and contravariance, and higher kinds:
data T a f = T1 a
| T2 (a → Int)
| T3 (f (T a f) (T a f))
These difficulties are frustrating, because we know that, say,
T Age Maybe is represented identically to T Int Maybe. So,
let us imagine a hypothetical extension of Haskell that provides
lifted coercions; that is, it implements the following rule:
1 There
are too many sorts of “=”!
In fact this extension is not so hypothetical, because such a rule is
the basis of the so-called “newtype deriving” feature implemented
by GHC. It is not difficult to use newtype deriving to define an
identity function whose type is
cvt :: ∀{c}. c Int → c Age
ML supports coercion lifting even more directly: within a structure the abstract and the representation types are considered entirely
interchangeable. For example in Standard ML one might say
signature AGE = sig
type age
val addAge : age → int → age
structure AgeModule :> AGE = struct
type age = int
fun addAge a n = a+n
Inside AgeModule the two types are synonymous, and so addAge
need not convert in either direction.
However, the point of this paper is that the innocuous and
obvious-seeming “lifting” of type identities becomes unsound
when combined with type-level computation, as we show in the
next section.
Type-level computation
One very popular extension to Haskell is that of Generalized Algebraic Data Types (GADTs) [Peyton Jones et al. 2006], with which
we assume the reader is somewhat familiar. In GHC one could declare a GADT with two nullary constructors like this:
data K a where
KAge :: K Age
KInt :: K Int
Now, consider these definitions (using cvt from Section 2.1):
kint :: K Age
kint = cvt KInt
get :: K Age → String
get KAge = "Age"
Since get’s type signature declares that its argument is of type
K Age, the patterns in get are exhaustive. But consider the call
(get kint). It is patently well typed, yet the pattern match in
get will fail (recall that cvt is operationally the identity function),
and if the compiler assumed otherwise a runtime crash could result.
In the last few years we have gone beyond GADTs, by extending GHC with type-level functions [Chakravarty et al. 2005a,b;
Kiselyov et al. 2010]. The reader is urged to consult these papers
for motivated examples of type functions, but for the purposes of
this paper we content ourselves with a small but contrived example:
type family F a :: *
type instance F Age = Char
type instance F Int = Bool
Here the type function F maps the type Age to the type Char, but
it maps Int to Bool.
However, the existence of such a type-level function threatens
not just pattern exhaustiveness but type soundness itself. Consider
the type Bool. This type is equivalent to F Int by the equation
for F; and by coercion lifting that should be equivalent to F Age;
and that is equivalent to Char by the other equation for F. Altogether we can coerce Bool to Char, which is obvious nonsense.
What went wrong? Maybe it should be illegal for a type function
to behave differently on two coercible types, such as Age and Int?
But in fact Haskell programmers often use newtypes precisely so
that they can give a different type-class instance (for comparison,
say) for Age than for the underlying Int. Type functions are no
different; indeed, they are often introduced as an “associated type”
of a type class [Kiselyov et al. 2010], and hence, just as the type
class distinguishes between the abstract and concrete type, so must
the type function.
How else might we fix the problem? Perhaps, in the definition of
coercion lifting we should not allow T to range over type functions
such as F or GADTs such as K? Indeed we should not, but that is
not enough. Consider
data TF a = MkTF (F a)
Now, should coercion lifting allow us to coerce TF Age to
TF Int? Obviously not! Otherwise we could write
to :: Bool → TF Int
to b = MkTF b
from :: TF Age → Char
from (MkTF c) = c
and now the composition from ◦ cvt ◦ to is well-typed (via
coercion lifting) but obviously unsound. So in the the definition
of coercion lifting, as well as type functions and GADTs we must
exclude data types like TF that use their type arguments nonparametrically.
At this point it should be clear that a naive combination of:
• type-level dispatch, whether by GADTs or by type functions
• unrestricted coercion lifting
simply does not work. This interaction was far from obvious to us
initially, and indeed GHC has a well-documented type-soundness
bug2 that arises directly from this unforeseen interaction. Yet both
type-level dispatch and coercion lifting (suitably restricted) are
valuable. The purpose of this paper is to show how they may be
soundly combined.
This problem is important not only because it arises in GHC
(which is our main source of motivation), but also because the same
issues will arise in any type system that combines type-level dispatch and coercion lifting. Haskell is the first programming language that has pushed the type system far enough for these problems to arise in practice, but others may do so in the future. However, these different languages may expose the coercions between
abstract and concrete types in different ways, possibly explicitly (as
in Haskell) or implicitly (as in ML), in a way that is intimately connected with type inference. To avoid the complications associated
with type inference, in this paper, we focus on an intermediate language, in which (runtime-erasable) coercions are explicit. Whether
they come directly from the source program, or from elaboration
by the type checker, is secondary.
The FC2 language: codes versus types
Our goal is to resolve the tension between generative types, which
allow programmers to express the intent that two types have identical representations, and type functions, which can distinguish two
types even if they have the same underlying representation.
The inspiration for our solution comes from encodings of
generic programming in dependently typed languages [Benke et al.
2003; Dybjer 2000]. These encodings use “codes” to represent
types as a form of data. For example, the code IntCode may
be the code for the type Int. Nonparametric functions branch on
codes and thus have a different type from parametric functions.
In this context, we can view Age and IntCode as two different
codes that both map to the type Int. However, this encoding requires significant syntactic overhead as we must have a code for
every type and must explicitly map codes to types when they are
used to classify expressions.
However, the important distinction between codes and types
is that they have different definitions of equality. In the encoding
above, the codes Age and IntCode are different codes, but their
interpretations are equal types. Therefore, we use this idea in FC2
to define roles, which support different notions of equality for the
same data. In this language, Age and Int are distinct when viewed
at role code but equal when viewed at role type. Code equality
is used to reason about the meaning of type-indexed functions
and is finer-grained than type equality, which is used to determine
which type coercions are safe. Importantly, FC2 distinguishes type
functions by what equivalences they respect. Parametric functions
respect the role type, whereas functions that distinguish between
Age and Int do not.
3.1 FC2 : an overview
These ideas are best explained in terms of an intermediate language that exposes the differences between the code and type roles
and makes explicit the uses of the two kinds of equality mentioned above. Thus, the remainder of this section describes FC2 ,
our new variant of System FC [Sulzmann et al. 2007]—a model
of the intermediate language used in GHC. As such, it is expressive enough to capture indexed type functions, newtype and
newtype deriving, GADTs, existential and nested datatypes,
and much more.
Figure 1 summarizes the syntax of FC2 , which, at the term
level (e), is just the polymorphic lambda calculus with two extensions. First, FC2 provides polymorphic datatypes, introduced by
data constructors K . These datatypes are eliminated using a case
construct that should be familiar from Haskell or ML-style functional programming—we describe how datatypes and case are
typechecked in Section 3.5.
Second, FC2 includes first-class proofs of type equality that
witness safe coercions introduced during compilation. Programs in
FC2 can abstract over coercions reflecting a particular type equality (written Λc : ϕ1 ∼ ϕ2 .e), pass a coercion as an argument to
such a function (written e γ), and use a coercion to cast a term
from one type to another (written e . γ). These explicit coercions,
written γ, make typechecking FC2 programs syntax-directed—that
is, the syntax of an FC2 term encodes its typing derivation. Why
is this important? The idea is that the compiler’s front end performs perhaps-complex type inference on the source program, and
records the proofs generated by inference directly in the syntax
of the FC2 intermediate language. The optimiser transforms FC2
terms, perhaps radically. At any point one can check the consistency of the resulting FC2 program using a simple, fast, syntaxdirected typechecker; this consistency check has proven to be an
extremely powerful aid to getting the compiler right. It is just as
easy to find the type of an arbitrary FC2 term, an ability that is
used extensively inside GHC.
Figure 2 shows the typing rules for the terms of FC2 . The first
five rules, EEVAR through ETA PP, are completely standard. We
defer explanation of the remaining rules until we build up technical
machinery having to do with FC2 ’s kind-level distinction between
codes and types and the rules by which explicit coercions can
themselves be combined. We describe these aspects of FC2 next.
kind and role
(∼η )
type constants
ϕ1 ϕ2
∀a : κ.ϕ
codes and types
sym γ
γ1 ; γ2
γ1 γ2
nth k γ
∀a : κ.γ2
coercion proof
λx :σ.e
e1 e2
caseσ e of brs
Λc : ϕ1 ∼ ϕ2 .e
e .γ
type abstraction
type application
data constructor
case analysis
proof abstraction
proof application
Ki ⇒ ei
H :η
c : ∆. ϕ1 ∼ ϕ2 /R
x :σ
K :σ
type variable
type constant
term variable
data constructor
· | Γ, bnd
· | a:κ, ∆
type context
e |ϕ|γ
datacon argument
σ, Θ
a:κ, Θ
ϕ1 ∼ ϕ2 , Θ
expression type
type variable
λx :σ.e | Λa:κ.e
Λc : ϕ1 ∼ ϕ2 .e
K ϕρ
T | (→) | (∼η )
∀a : κ.ϕ | τ ϕ
value types
ϕ, σ, ψ
x :σ ∈ Γ ` Γ
Γ`x :σ
Γ, x :σ1 ` e : σ2
Γ ` λx :σ1 .e : σ1 → σ2
Γ ` e1 : σ1 → σ2 Γ ` e2 : σ1
Γ ` e1 e2 : σ2
Γ, a:κ ` e : σ
Γ ` Λa:κ.e : ∀a : κ.σ
Γ ` e : ∀a : κ.σ Γ ` ϕ : κ
Γ ` e ϕ : σ[a 7→ ϕ]
T :κ → ? ∈ Γ
K :∀ a : κ.∀ Θ.T a ∈ Γ ` Γ
Γ ` K : ∀ a : κ.∀ Θ.T a
Γ ` e : T ϕ Γ ` σ : ?/T
for each Ki ∈ ConstrΓ (T )
Ki :∀ a : κ.ψi ∈ Γ
ψi [a 7→ ϕ] = ∀ Θi .T ϕ
Γ ` ei : ∀ Θi .σ
Γ ` caseσ e of Ki ⇒ ei : σ
Γ, c:ϕ1 ∼ ϕ2 /C ` e : σ
Γ ` Λc : ϕ1 ∼ ϕ2 .e : (ϕ1 ∼ ϕ2 ) ⇒ σ
Γ ` e : (ϕ1 ∼ ϕ2 ) ⇒ σ
Γ ` γ : ϕ1 ∼ ϕ2 ∈ η/C
Γ`eγ :σ
Γ ` e : σ1
Γ ` γ : σ1 ∼ σ2 ∈ ?/T
Γ ` e . γ : σ2
Figure 2. Typing rules
R1 R2
a:η/R1 ∈ Γ R1 R2
Γ ` a : η/R2
H :η ∈ Γ ` Γ
Γ ` H : η/R
Γ ` ϕ1 : (η1 /R2 → η2 )/R1
Γ ` ϕ2 : η1 /min(R1 , R2 )
Γ ` ϕ1 ϕ2 : η2 /R1
Γ, a:κ ` ϕ : ?/R
Γ ` ∀a : κ.ϕ : ?/R
Γ`ϕ:κ Γ`ϕ:κ
Γ ` ϕ, ϕ : κ, κ
Figure 3. Kinding
Figure 1. Syntax
3.2 FC2 types and kinds
Types in FC2 are classified by pairs κ of the form η/R, where the
kind η ensures (as usual) that types are well-formed structurally,
and the role R that determines the precision at which they can be
analyzed. Codes (which distinguish Age and Int) have role C,
whereas types (which identify them) have role T. This syntax is
summarized at the top of Figure 1, while the kinding rules are in
Figure 3.
The distinction between codes and types allows us to give informative kinds to type constructors:
• The Maybe type (Section 2.1) has kind ?/T → ?, indicating
that Maybe treats its argument parametrically.
• The types K, F, and TF (Section 2.2) all use type indexing and
newtypes are injective at role C—after all, the essence of generativity is that newtypes create a fresh constant—but for now we will not
take advantage of that fact, leaving it for discussion in Section 6.1.
The set of type constants also includes the familiar arrow type
constructor (→), and a kind-indexed family of constructors (∼η ),
which construct functions that abstract over coercions. The kinds
of these constants are:
(→) : ?/T → ?/T → ?
(∼η ) : η/C → η/C → ?/T → ?
(We discuss in Sections 3.3 and 3.6 why the kind of (∼η ) must
require role C for its first two arguments.) Type constants are
generally applied prefix, but for these two constants we define infix
syntactic sugar:
σ → σ0
(ϕ1 ∼ ϕ2 ) ⇒ σ
therefore have kind ?/C → ?.
These kinds in turn support the key insight of this paper: it is only
safe to lift coercions through functions with parametric kinds. So
Maybe Age ∼ Maybe Int holds but TF Age 6∼ TF Int.
The rules for constructing coercions are given in Section 3.3,
but first we must deal with the kinding rules for types. The critical
rule is PA PP in Figure 3, which deals with type application. It is
quite conventional except for the treatment of roles. Consider these
type declarations:
data TF1 a = MkTF1 (F (Maybe a)
data TF2 a = MkTF2 (Maybe (F a))
data TF3 a = MkTF3 (Maybe (Maybe a))
In the first two cases, type variable a is used non-parametrically
(intuitively, under a type function), so TF1 : ?/C → ?, and
similarly for TF2. In contrast, TF3 treats a parametrically, so
TF1 : ?/T → ?. The unusual “min” in rule PA PP achieves these
kindings by combining the role of the context and the argument role
of the function to get the role of the argument.
In fact, min is just the least upper bound induced by an inclusion relation CT on roles (see the top of Figure 3). This inclusion
makes explicit the fact that code equality implies type equality but
not vice-versa. For example, consider
data TF4 a = MkTF4 (F a) a
Here a is used both non-parametrically (as an argument of F) and
parametrically (as a plain argument of MkTF4). So a has role ?/C,
which makes sense; just because a type can be analyzed does not
mean that it must be.
Despite these non-standard kinds, the types of FC2 are mostly
standard. Codes ϕ and types σ are drawn from the same syntax
(see Figure 1), although we use two different metavariables as a reminder of the intended role. The type language includes type variables a, type constants H , applications ϕ1 ϕ2 , and polymorphic
types ∀a : κ.σ.
Type constants, H , include datatypes T , and type functions
F . For the most part, datatypes and type functions are treated
uniformly, but there are two important distinctions:
• Datatypes must be injective, while type functions need not be.
Injectivity is important because equalities between applications
of injective functions imply equalities between their arguments;
see rule CN TH T in Section 3.3.
• Datatypes are inhabited by values, but type functions are not—
there are no values v with types that are headed by F . (There
are coerced values with such types, but no raw values.) This
distinction is key to the definition of consistency in Section 4.1.
(→) σ σ 0
(∼η ) ϕ1 ϕ2 σ
In the latter case, because the syntactic sugar lacks the η annotation,
we only use this notation in contexts where the kind of ϕ1 and ϕ2
is irrelevant. Rule EC ABS follows this convention—it shows that
this family of type constructors is used to give a type to terms of
the form Λc : ϕ1 ∼ ϕ2 .e, which abstracts over an equality proof
in the body e. Note that this rule applies only to code equalities;
abstraction over type equalities is not needed for compilation of
Haskell and permitting it, while straightforward, would require
extra syntactic complexity.
Figure 3 defines the kinding rules for FC2 using judgments of
the form Γ ` ϕ : κ. Here, the context Γ maps type variables
to their kind/role pairs and type constants and type functions to
their kinds. This judgment relies on a judgment ` Γ (shown in the
appendix) that checks that contexts are well formed. These checks
ensure that constants (→) and (∼η ) have the right kinds, that data
constructors and term variables have well-formed types and that
coercion axioms introduce equalities between well-formed codes
and types.
Rule PC ONST allows a type constant to play either role. Although a type constant may be given role C, its kind may well involve arguments with role T; for example, see the signature for
(→) above. It follows that closed types can play both roles, independently of whether they contain newtypes or type functions. For
example (Int,Age) : ?/C and (Int,Age) : ?/T both hold.
Together these kinding rules implement a subsumption relation
that includes codes into the language of types:
L EMMA 1. If Γ ` ϕ : η/C then Γ ` ϕ : η/T.
On the other hand, types have only one kind regardless of their role:
L EMMA 2. If Γ ` ϕ : η1 /R1 and Γ ` ϕ : η2 /R2 then η1 = η2 .
Note that the kinding judgment, like term typing, is syntaxdirected (see the remarks in Section 3.1). In particular, the role
component R of the κ in this judgment is treated as input to the
algorithm, and the η is an output—the context in which ϕ is used
determines what the role should be. The only interesting case from
this perspective is PA PP, in which ϕ1 must be checked first to
obtain R2 so that the minimum of R1 and R2 can be passed as
an input when checking ϕ2 .
Coercions and equality
In FC2 a coercion is a proof term that witnesses the equality of
two types. Coercions are used to change the type of a term, thus
(Figure 2):
Γ ` e : σ1
Newtypes are not inhabited by raw values, so we treat them like
type functions, ranged over by F . Unlike type functions, however,
Γ ` γ : σ1 ∼ σ2 ∈ ?/T
Γ ` e . γ : σ2
Here, γ is a coercion witnessing the equality σ1 ∼ σ2 in role T;
given that e has type σ1 , we can use γ to let us treat the term as hav-
More generally we permit axiom schemes. For example, the
source language declaration
Γ ` γ : ϕ1 ∼ ϕ2 ∈ κ
(c : a : κ. ϕ1 ∼ ϕ2 /R1 ) ∈ Γ
Γ ` ϕ1 : η/R1 Γ ` ψ : κ R1 R2
type instance F(Maybe a) = (a,a)
Γ ` c ψ : ϕ1 [a 7→ ψ] ∼ ϕ2 [a 7→ ψ] ∈ η/R2
would create the axiom scheme
axF3 : (a : ?/C). F(Maybe a) ∼ (a,a)/C
In general, as shown in Figure 1, the context Γ includes bindings of
the form c : ∆. ϕ1 ∼ ϕ2 /R for coercion axioms. The metavariable
∆ stands for a type variable context: a list of quantified type
variable bindings of the form a:κ. The same binding form is used
both for axioms introduced at top level, and (with empty ∆) for
local assumptions introduced in ECA BS (Figure 2).
Of course it is important to know that the top-level axioms are
consistent—it would be unsound to assert that Bool ∼ Char/T,
for example. Section 4 gives a sufficient set of conditions for ensuring that source programs generate consistent axioms.
Next, we need a way to compose coercions together to construct
other coercions. Our goal is to have rules that allow the creation of
composite coercions such as:
Γ ` γ : ϕ1 ∼ ϕ2 ∈ κ
Γ ` sym γ : ϕ2 ∼ ϕ1 ∈ κ
Γ ` γ1 : ϕ1 ∼ ϕ2 ∈ κ
Γ ` γ2 : ϕ2 ∼ ϕ3 ∈ κ
Γ ` γ1 ; γ2 : ϕ1 ∼ ϕ3 ∈ κ
Γ ` γ1 : ϕ1 ∼ ϕ2 ∈ (η1 /R2 → η2 )/R1
Γ ` γ2 : ψ1 ∼ ψ2 ∈ η1 /min(R1 , R2 )
Γ ` γ1 γ2 : ϕ1 ψ1 ∼ ϕ2 ψ2 ∈ η2 /R1
Γ ` γ : T ϕ ∼ T ψ ∈ η/T
T :κ → ? ∈ Γ
η 0 /R1 = nth k κ R1 R2
List mkAge
List axF2
Γ ` nth k γ : nth k ϕ ∼ nth k ψ ∈ η 0 /R2
Γ, a:κ ` γ2 : ϕ1 ∼ ϕ2 ∈ ?/R
Γ ` ∀a : κ.γ2 : ∀a : κ.ϕ1 ∼ ∀a : κ.ϕ2 ∈ ?/R
Γ ` γ : ∀a : κ.ϕ1 ∼ ∀a : κ.ϕ2 ∈ ?/R
Γ ` γ@ψ : (ϕ1 [a 7→ ψ]) ∼ (ϕ2 [a 7→ ψ]) ∈ ?/R
Γ ` γ : ϕ1 ∼ ϕ2 ∈ κ
Γ ` γ : ϕ1 ∼ ϕ2 ∈ κ
Γ ` γ : ϕ1 ∼ ϕ2 ∈ κ
Γ ` γ, γ : ϕ1 , ϕ1 ∼ ϕ2 , ϕ2 ∈ κ, κ
Figure 4. Coercions
ing type σ2 . At compile time, these explicit coercions ensure that
typechecking FC2 programs is completely syntax directed. Such
coercions have no run-time effect: they will be erased by the compiler before the program is run. Nevertheless, FC2 ’s operational
semantics includes coercions, thereby allowing us to establish type
safety using standard techniques (Section 3.6).
The translation of a source program into FC2 may extend the
type environment Γ with new equality axioms. For example, the
Age newtype definition generates the axiom (where mkAge is a
coercion constant c):
mkAge : Age ∼ Int/T
Note that EC OERCE requires σ1 and σ2 to be equal when
considered in role T, which is consistent with the idea that type
equality determines when it is safe to coerce. On the other hand,
source programs can also introduce code equalities. For example
the type function F (Section 2.2) generates the two axioms:
List Age ∼ List Int/T
List(F Int) ∼ List Bool/T
On the other hand, the coercion formation rules should disallow
the formation of a coercion of the form γ3 : F Age ∼ F Int/T,
which creates the unsoundness described in Section 2.2.
Figure 1 gives the syntax of coercion terms, γ, and Figure 4
gives their typing rules. Coercions are typechecked using the judgment: Γ ` γ : ϕ1 ∼ ϕ2 ∈ η/R, which asserts that the type of a
coercion γ is an equality proposition ϕ1 ∼ ϕ2 ∈ η/R. This proposition in turn implies that ϕ1 and ϕ2 both have kind η and are equal
in role R. It is technically convenient to include η in the syntax of
the judgement to enforce that both types have the same kind. However, this component is not always relevant, so we sometimes omit
the ∈ η part, as we have done in the examples above.
Rule CA SSM instantiates an axiom scheme with types ψ, using
an auxiliary judgment Γ ` ψ : κ defined at the bottom of Figure 3 to ensure that each variable is instantiated with a type of the
matching kind and role. The notation a : κ zips together a list of
type variables and a list of kinds to create a type variable context
∆. These two lists must have the same length for the notation to be
well-defined. The notation ϕ[a 7→ ψ] applies a multi-substitution
of the types ψ for each of the corresponding variables in the list a.
Rule CR EFL shows that any type ϕ can be lifted to a reflexive
coercion ϕ, while CS YM and CT RANS add symmetry and transitivity, ensuring that equality is an equivalence relation. The rules
CA PP and CA LL extend equality compatibly over applications and
polymorphic types; their structure is analogous to the corresponding kinding rules in Figure 3. Rule CA PP is particularly important,
because it implements the key coercion lifting idea we discussed
in Section 2.1, using kinds to prevent the formation of the bogus
FmkAge : F Age ∼ F Int/T
To see why, recall that F has kind ?/C → ?, but the mkAge axiom
holds only at role T—type equalities cannot be lifted through code
functions. Another example of a coercion that is correctly rejected
by the application rule (because of the kind of (∼η )) is
(∼? ) mkAge Int σ
However, all code axioms can be used to prove type equalities, as
code equality is a refinement of type equality.
This coercion proves (Age ∼ Int) ⇒ σ ∼ (Int ∼ Int) ⇒ σ,
an equality that could be used to introduce a bogus assumption that
Age ∼ Int/C and satisfy it with reflexivity for Int.
As well as composing coercions to witness the equality of bigger types, it is also essential to do the reverse: to decompose
equalities over complex types to give equalities of simpler types.
axF1 : F Int ∼ Bool/C
axF2 : F Age ∼ Char/C
Decomposition is required by FC2 ’s operational semantics (Section 3.6), and it also makes the language usefully more expressive. Rule CI NST allows equalities between polymorphic types to
be instantiated. The other, more important decomposition rule is
CN TH T, which decomposes the application of a datatype constant
to arguments. For example, given a coercion γ : List Int ∼
List a/T we can use nth 0 γ to conclude that Int ∼ a/T.
The soundness of this rule depends on the fact that datatypes are
injective. In general, type functions are not, and hence CN TH T is
restricted to datatypes T .
In rule CN TH T, the notation T ϕ abbreviates the multi-application
((T ϕ1 ) . . . ϕn ) for ϕ1 . . . ϕn in ϕ. In the conclusion of the rule,
the notation nth k ψ, accesses the kth element of the sequence of
types, and nth k κ, accesses the kind of the kth variable binding.
Both of these notations are undefined if k is not less than the length
of the sequence. The sequence of types κ is determined by the kind
of the datatype, using the following notation.
FC2 includes a formalization of recursive datatypes. These datatypes
include all GHC extensions to standard datatypes: empty datatypes,
nested datatypes, existential types, first-class polymorphism and
GADT S. Both datatypes T and data constructors K must be declared in a context Γ before they can be used. For example, using
the syntax for Γ in Figure 1, the declarations for the data constructors of List are:
List : ?/T → ?
: (a: ? /T). List a
Cons : (a: ? /T). a → List a → List a
D EFINITION 3. We define κ → η by induction on κ as follows:
What about GADTs? Here’s an example in Haskell:
κ, κ → η
κ → (κ → η)
The coercion judgment satisfies a number of sanity checking
properties. First, coercions are between well-formed types.
L EMMA 4 (Coercion regularity). If Γ ` γ : ϕ1 ∼ ϕ2 ∈ κ then
Γ ` ϕ1 : κ and Γ ` ϕ2 : κ.
Next, each coercion proof is for one pair of codes/types.
L EMMA 5 (Unique types). If Γ ` γ : ϕ1 ∼ ϕ2 ∈ η/R and
Γ ` γ : ϕ01 ∼ ϕ02 ∈ η 0 /R then ϕ1 = ϕ01 and ϕ2 = ϕ02 and
η = η0 .
Finally, equality for Cs is a refinement of that for Ts; that is, code
equality implies type equality, but not vice versa.
L EMMA 6. If Γ ` γ : ϕ1 ∼ ϕ2 ∈ η/C then Γ ` γ : ϕ1 ∼ ϕ2 ∈
Coercion lifting
The application rule CA PP described in the previous section allows
us to lift equalities through arbitrary type constructors; that is, for
all datatypes T of kind ?/T → ?, we have a coercion that shows
T Age ∼ T Int ∈ ?/T.
In fact, this notion of coercion lifting is not restricted to
datatypes (like List), but is available for more general contexts.
More precisely, given an arbitrary type σ with free variable a of
kind ?/T, we can also create a coercion σ[a 7→ Age] ∼ σ[a 7→
Int] ∈ ?/T.
We create such coercions with the lifting operation. This operation replaces type variables by coercions in types to produce a new
coercion, relying on the fact that the syntax of types is a subset of
the syntax of coercions:
D EFINITION 7 (Lifting Operation). Define the lifting operation,
written ϕ[a 7→ γ], by induction on ϕ.
a[a 7→ γ]
b[a 7→ γ]
H [a 7→ γ]
(ϕ ψ)[a 7→ γ]
(∀b : κ.σ)[a 7→ γ]
(ϕ[a 7→ γ]) (ψ[a 7→ γ])
∀b : κ.(σ[a 7→ γ])
when a 6= b
In other words, if a were used in some indexed context in σ,
that is, if its role were C, then we would not be able to lift the
coercion Age ∼ Int ∈ ?/T in σ. We generalize lifting to replace
multiple type variables simultaneously in the obvious way, with
notation σ[a 7→ γ].
Pattern matching and datatypes
data Rep a where
Rint :: Rep Int
Rlist :: Rep a → Rep (List a)
Although a Haskell programmer writes the data constructors of
a GADT with non-parametric result types, in the internal type
system it is simpler in the formalization for the result type of a
data constructor to take the form (T a1 . . . an ), where the a are the
type parameters, using equality constraints to express the indexing,
Rep : ?/C → ?
Rint : (a: ? /C).(a ∼ Int) ⇒ Rep a
Rlist : (a: ? /C).∀ (b : ?/C).(a ∼ List b) ⇒ Repb → Rep a
Notice here that Rep’s kind expresses that its argument is an index
(role C) rather than a parameter (role T). In fact, the C role for
variable a falls out naturally because a appears as argument to the
(∼? ) constructor (in the type of Rint and Rlist), which in turn
requires it to have role C.
More generally, we use the notation of telescopes [de Bruijn
1991] to conveniently express the types of data constructors. Figure 1 defines a telescope Θ like this:
Θ ::= · | a:κ, Θ | ϕ1 ∼ ϕ2 , Θ | σ, Θ
A telescope is like a mini-context: a list of type variable bindings,
equality propositions (between codes only) and types that classify
each argument of the data constructor. (Note that a type variable
context ∆ is also a telescope.) We define the syntactic sugar ∀ Θ.σ
as follows:
D EFINITION 9 (Telescope syntactic sugar).
∀ · .σ
∀ (a:κ, Θ).σ
∀ (ϕ1 ∼ ϕ2 , Θ).σ
∀ (σ 0 , Θ).σ
∀a : κ.(∀ Θ.σ)
(ϕ1 ∼ ϕ2 ) ⇒ (∀ Θ.σ)
σ 0 → (∀ Θ.σ)
At each use of a data constructor (rule EDATAC ON), we check that
the declaration of a data constructor is of the form
K :∀ a : κ.(∀ Θ.T a)
L EMMA 8 (Lifting). If Γ, a:η/R ` σ : κ and Γ ` γ : ϕ ∼ ϕ0 ∈
η/R, then Γ ` σ[a 7→ γ] : σ[a 7→ ϕ] ∼ σ[a 7→ ϕ0 ] ∈ κ.
where a : κ lists the type parameters of the datatype, Θ describes
the types of the arguments to the data constructor, and T a is
syntactic sugar for the application of T to those parameters (i.e.
((T a1 ) . . . an )). For example, the List type has a single parameter a: ? /T, the constructor Cons has telescope (a, List a, ·),
and constructs type List a. Likewise, the Rep type has the
parameter a: ? /C, the telescope for Rlist is (b: ? /C, a ∼
List b, (Rep b), ·), and the result type is Rep a.
The lifting operation produces a valid result as long as the role of
the lifted coercion matches the role of the type variable in the type.
γ0 = sym (nth 0 γ) γ1 = nth 1 γ
((λx :σ1 .e1 ) . γ) e2
(λx :σ1 .(e1 . γ1 )) (e2 . γ0 )
Σ ` γ : ∀a : κ.σ1 ∼ ∀a : κ.σ2 ∈ ?/T
((Λa:κ.e) . γ) ϕ
(Λa:κ.(e . γ@a)) ϕ
(∼η ) ϕ01
Γ`e:σ Γ`ρ:Θ
Γ ` e, ρ : (σ, Θ)
Σ ` γ : (∼η ) ϕ1 ϕ2 σ ∼
σ ∈ ?/T
γ0 = nth 0 γ γ1 = sym (nth 1 γ) γ2 = nth 2 γ
((Λc : ϕ1 ∼ ϕ2 .e) . γ) γ 0
(Λc : ϕ1 ∼ ϕ2 .(e . γ2 )) (γ0 ; γ 0 ; γ1 )
Figure 5. Operational Semantics (Push rules)
Telescope notation is also used for case expressions in rule
EC ASE (Figure 2). The type of the scrutinee of the case must
be headed by a datatype constant T . Furthermore, for each data
constructor that could create a T , there must be a corresponding
branch. The (elided) function ConstrΓ (T ) looks up the constructors of T from the context. After substituting for the parameters,
the branch for data constructor Ki must abstract the same arguments as Ki and return the same result type as the entire case. To
make sure that typechecking is syntax directed even when there
are no branches (for empty datatypes), the case expression is annotated with its result type σ, and we must check that this type is
well-formed in the current context.
Γ ` ϕ : κ Γ ` ρ : Θ[a 7→ ϕ]
Γ ` ϕ, ρ : (a:κ, Θ)
Γ ` γ : ϕ1 ∼ ϕ2 ∈ η/C Γ ` ρ : Θ
Γ ` γ, ρ : (ϕ1 ∼ ϕ2 , Θ)
Σ ` γ : T ϕ ∼ T ϕ ∈ ?/T
K :∀ a : κ.σ ∈ Σ
caseσ (K ϕ ρ) . γ of brs
case0σ K ϕ0 (ρ . σ[a 7→ nth γ]) of brs
Operational semantics: pushing coercions
The operational semantics of FC2 is based on a small-step callby-name operational semantics for a polymorphic lambda calculus. Although this language includes explicit type abstraction and
application (as well as explicit coercion proof abstraction and application), types and coercions are not relevant at run time. All such
abstractions and applications, as well as uses of coercions e .γ may
be erased prior to execution and so impose no run-time costs.
As alluded to above, this operational semantics preserves coercion proofs, which allows us to establish type safety using standard
syntactic proofs of progress and preservation (described in the next
section). The most important rules of the operational semantics are
those that “push” coercions when they appear in active positions
so that they do not interfere with reduction. Figure 5 shows the
relevant pushing rules. (The complete rules of the operational semantics are listed in the appendix.) In some of the push rules, the
coercion must be checked to constrain the types that it proves equal.
This checking happens in a global context Σ that contains only the
definitions of data constructors, data types, and coercion axioms.
Note that these checks are not actually necessary at runtime, in a
consistent context these checks will always succeed.
The first three rules show how in an application of a coerced
abstraction, the term steps to a new application, where the coercion has been decomposed into a coercion for the body of the abstraction, and a coercion of the argument. For example, consider
SSP USH. Here, the γ is a coercion between some function types
σ1 → σ10 and σ2 → σ20 . The rule uses nth and sym to decompose
γ into two coercions, one from σ2 ∼ σ1 (the order is reversed to
account for contra-variance) and one from σ1 ∼ σ20 . These new
coercions can be pushed to the body of the lambda and the function
argument, exposing the reduction. Rules SSTP USH and SSCP USH
work analogously.
Figure 6. Telescope rules
Note that SSCP USH justifies the kind of (∼η ), which requires
that the first two arguments be codes. If we had assigned (∼η ) the
parametric kind η/T → η/T → ?/T → ?, then the coercions
γ0 and γ1 in the rule would both be type coercions. However, type
coercions cannot be composed with γ 0 to form a code coercion,
which is the role required for the right hand side of the rule to
The last rule, SSKP USH, pushes the coercion of a data constructor in the scrutinee position of a case expression into coercions
of the arguments of the data constructor. The arguments of a data
constructor, notated ρ, can either be an expression, a type, or a coercion.
ρ ::= e | ϕ | γ
If the declared type of the data constructor K is ∀ a : κ.∀ Θ.T a,
and the type parameters are ϕ, then we know that the arguments
can be typed using Γ ` ρ : Θ[a 7→ ϕ] (see Figure 6). However,
pushing the coercion changes the type parameters to be ϕ0 , so the
new arguments must have type Θ[a 7→ ϕ0 ].
These new arguments are produced by coercing the list of arguments ρ with the coercion generated by lifting as described above.
(The notation σ[a 7→ nth γ] means that variable ai is lifted to coercion nth i γ.) Once this coercion has been defined by lifting, we
use it to coerce the list of arguments of the data constructor with
the following operation.
D EFINITION 10. Define argument coercion ρ . γ by induction on
(e, ρ) . γ1 → γ2
(ϕ, ρ) . ∀a : κ.γ2
(γ, ρ) . (γ1 ∼ γ2 ) ⇒ γ3
(e . γ1 ), ρ . γ2
ϕ, ρ . γ2
(sym γ1 ; γ ; γ2 ), ρ . γ3
Argument coercion coerces a list of arguments as described by
the following lemma.
L EMMA 11. If Γ ` ρ : Θ and Γ ` γ : ∀ Θ.σ ∼ ∀ Θ0 .σ 0 ∈ ?/T
then Γ ` (ρ . γ) : Θ0
Type safety and consistency
The FC2 language supports a straightforward proof of type safety
based on the usual preservation and progress theorems. Importantly, the progress theorem holds only for consistent contexts—
those that cannot equate Int and Char, for example. Below, we
state the progress and preservation theorems and give a precise definition of consistency. In the next subsection, we formulate sufficient conditions for proving that a context is consistent.
Preservation and progress
ϕ0 ∈ κ
The preservation proof for FC2 is standard, relying on the usual
regularity and substitution lemmas for the various judgement
forms. For space reasons, we omit those definitions here and instead refer the reader to the appendices.
T HEOREM 12 (Preservation). If Γ ` e1 : σ and e1
Γ ` e2 : σ.
Γ ` a : κ → η/R
ϕ0 ∈ κ/R
a ϕ0 ∈ η/R
e2 then
H :κ → η ∈ Γ
ϕ0 ∈ κ/R
H ϕ0 ∈ η/R
The progress theorem holds only for closed, consistent contexts.
A context is closed if it does not contain any term variable bindings.
We use the metavariable Σ for closed contexts.
The definition of consistency and the canonical forms lemma
(necessary to show the progress theorem) are both stated using the
notions of uncoerced values and their types, value types. Value
types include all types except those that are headed by a variable
a or type function/newtype F . Formally, we define values v and
value types τ , with the following grammars:
T | (→) | (∼η ) | ∀a : κ.ϕ | τ ϕ
λx :σ.e | Λa:κ.e | Λc : ϕ1 ∼ ϕ2 .e | K ϕ ρ
is T ϕ1 then τ2 is T ϕ2 .
is (→) ϕ1 then τ2 is (→) ϕ2 .
is (∼η ) ϕ1 then τ2 is (∼η ) ϕ2 .
is ∀a : κ.σ1 then τ2 is ∀a : κ.σ2 .
Putting these observations together, we obtain:
T HEOREM 15 (Progress). If Σ is consistent and Σ ` e1 : σ and
e1 is not a value v or a coerced value v . γ, then there exists an e2
such that e1
e2 .
· ∈ ·/R
ϕ0 ∈ η/min(R1 , R2 )
ϕ0 ∈ κ/R1
Γ ` ϕ, ϕ
ϕ0 , ϕ0 ∈ η/R2 , κ/R1
Figure 7. Type rewriting
D EFINITION 14 (Consistency). A context Γ is consistent if whenever Γ ` γ : τ1 ∼ τ2 ∈ η/T it is the case that
If τ1
If τ1
If τ1
If τ1
ϕ0 ∈ κ/R
= σ1 → σ2 then v is λx :σ1 .e or K ϕ ρ.
= ∀a : κ.σ 0 then v is Λa:κ.e or K ϕ ρ.
= (ϕ1 ∼ ϕ2 ) ⇒ σ 0 then v is Λc : ϕ1 ∼ ϕ2 .e or K ϕ ρ.
= T ϕ1 then v is K ϕ ρ.
In FC2 , not all irreducible forms are values. Evaluation can also
produce a coerced value of the form v . γ, which erases to a value
when coercions are dropped. To prove the progress theorem, we
must reason about what sort of coercion γ could be so that we can
appropriately apply the “push” rules in Figure 5. In the statement
of the progress theorem, because γ coerces the value v , we know
(by EC OERCE and canonical forms) that γ : τ ∼ σ—the left type
is a value type τ . Consistency of the axiom set assures us that if σ
is also a value type, it must have the same head form.
H :κ1 → (κ2 → η) ∈ Γ
Γ ` ϕ1
ϕ01 ∈ κ1 /R
Γ ` ϕ2
ϕ02 ∈ κ2 /R
Γ ` c ψ : H ϕ01 ∼ σ ∈ κ2 → η/R
Γ ` H ϕ1 ϕ2
σ ϕ02 ∈ η/R
L EMMA 13 (Canonical Forms). Say Σ ` v : σ. Then σ is a value
type. Furthermore,
If σ
If σ
If σ
If σ
Γ, a:κ ` σ
σ 0 ∈ ?/R
Γ ` ∀a : κ.σ
∀a : κ.σ 0 ∈ ?/R
The canonical forms lemma tells us that the shape of a value is
determined by its type:3
Determining consistency
Although the previous subsection gives a definition of when contexts are consistent, it does not provide any mechanism for determining whether a set of axioms leads to a consistent context.
This subsection defines sufficient conditions (written Good Γ)
for establishing context consistency—these conditions are not the
only way to show consistency (they are not necessary) but they are
permissive enough to cover the axioms generated by compilation
of type family declarations and newtype definitions.
As in the previous version of FC, we show consistency by
(i) defining a rewrite system for types and (ii) proving that two
types are joinable (share a common reduct) if and only if there
is some coercion proof between those types. The rewrite system
guarantees that value types preserve their head form throughout
rewriting and therefore value types with different head forms can
never be equated.
Rewriting and joinability
Figure 7 gives our rewrite relation, which is a variant of parallel
reduction—it looks throughout the type, trying to fire reductions.
The reduction of a type headed by a constant H is implemented in
rule RR ED. If, after the arguments to H have been reduced to ϕ0 ,
there is some instantiation of an axiom such that H applied to some
prefix of the ϕ0 matches the left-hand side of the coercion, then the
type may reduce to the right-hand-side type (RR ED). Importantly,
rewriting (very much like coercibility) takes roles into account:
rewriting occurs at some role R, which specifies what axioms are
available. For example, at role T we can rewrite a newtype Age to
its definition Int, but at role C, we cannot.
Notice that rules RC ONST and RR ED overlap. The term H ϕ
may reduce using a matching axiom for H (via rule RR ED), but not
necessarily (via RC ONST). With rule RC ONST, the rewrite relation
is reflexive.
L EMMA 16. If Γ ` ϕ : κ then Γ ` ϕ
ϕ ∈ κ.
The joinability relation, below, is simply reduction to a common
reduct after zero or more steps.
that all forms of value type must include partial applications of data
D EFINITION 17 (Joinable). Two types are joinable if they share a
common reduct. Define Γ ` ϕ1 ⇔ ϕ2 ∈ κ if Γ ` ϕ1 ∗ ϕ ∈ κ
and Γ ` ϕ2 ∗ ϕ ∈ κ.
3 Note
Reflexivity of the rewrite relation is crucial for joinability of type
applications with multiple arguments. Consider the axiom
c : F Int ∼ (F Int, F Int)/C
L EMMA 22 (Substitution). If Good Γ and Γ, a:κ, ∆ ` σ
σ 0 ∈ κ0 and Γ ` ϕ
ϕ0 ∈ κ, then there is some Γ, ∆ `
σ[a 7→ ϕ] ⇔ σ 0 [a 7→ ϕ0 ] ∈ κ0 .
From these lemmas we see that joinability is complete.
and the following two types:
T HEOREM 23 (Completeness). If Good Γ and Γ ` γ : ϕ1 ∼
ϕ2 ∈ κ then Γ ` ϕ1 ⇔ ϕ2 ∈ κ.
ϕ1 = T (F Int) (F Int, F Int)
ϕ2 = T (F Int, F Int) (F Int)
We’d certainly like ϕ1 and ϕ2 to be reducible to a common reduct
(after all, there exists a coercion term between them), but the first
argument of T in ϕ1 “lags behind” compared to the first argument
of T in ϕ2 , whereas the second argument in ϕ1 “advances ahead”
compared to the second argument in ϕ2 . Reflexivity allows us to
freeze the reduction of the second argument in ϕ1 while reducing
the first, and similarly for ϕ2 —which allows us to join the two
Soundness and completeness of the rewrite relation
We now give sufficient conditions on contexts which ensure that the
rewrite relation of Figure 7 is sound and complete with respect to
coercibility. Hence, these are sufficient conditions for consistency.
D EFINITION 18 (Good contexts). We have Good Γ when the following conditions hold:
1. All axioms in Γ are of the form c : ∆. F ϕ ∼ ψ/R. Let
a = dom(∆) and κ be the corresponding kinds. For every well
kinded ψ and result types ϕ0 , such that Γ ` (ϕ[a 7→ ψ])
ϕ0 ∈ κ0 /R it must be ϕ0 = ϕ[a 7→ ψ ] for some ψ with
ψ ∈ κ/R.
2. There is no overlap between axioms. For each F ϕ there exists
at most one prefix ϕ1 of ϕ such that there exist c, ψ and ψ where
Γ ` c ψ : F ϕ1 ∼ ψ ∈ κ. This c is unique for every matching
F ϕ1 .
The first condition above restricts the declared arguments of
type functions to behave like patterns. It merely states that all the
reductions that can possibly happen when we substitute types for ∆
do not involve reductions from ϕ but only reductions inside those
substituted types. This condition is a generalized (and more lenient)
form of the current GHC restrictions on type family declarations,
which only allow value types or variables as ϕ. The second condition is simply a strong non-overlapping condition.
Interestingly, the first condition on the arguments to type functions restricts the kind that a type function may have. For example,
recall the axioms for F from Section 2.2:
axF1 : F Int ∼ Bool/C
axF2 : F Age ∼ Char/C
T HEOREM 24 (Consistency). If Good Γ then Γ is consistent.
Proof Suppose Γ ` γ : τ1 ∼ τ2 ∈ ?/R, where τ1 and τ2 are two
value types. By completeness, we have that those two types are
joinable, i.e. that there is some ϕ such that Γ ` τ1 ∗ ϕ ∈ ?/R
and Γ ` τ2 ∗ ϕ ∈ ?/R. However, by inversion on the rewriting
relation, we see that it preserves the head forms of value types
(since there exist no axioms for those by the first condition of
Good Γ). Thus, τ1 and τ2 (and ϕ) have the same head form.
A different novelty of our approach compared to previous
work [Sulzmann et al. 2007] is that establishing the completeness theorem, and therefore type soundness, does not depend on
strong normalization. Our definition of Good Γ that (i) forces the
declared type family arguments to behave like patterns, and (ii) imposes strong (stronger than the previous work) non-overlapping
conditions, is sufficient to show completeness.
With this new approach, a programmer can be confident that,
even in the presence of possibly non-terminating type functions,
if the compiler can show that the program is well-typed then the
program will not crash. Although type checking is undecidable in
general without strong normalization, there may be many programs
that the compiler can actually type check, and this approach shows
that those programs are type sound.
Compilation from source Haskell
In the previous sections we have informally presented the translations of Haskell source features such as datatype, type family, and
newtype declarations into FC2 . We summarize them here:
data T ∆ where Ki : σ
T : ∀∆.?, Ki : ∆.σ
type family F : η/C → η
F : η/C → η
type instance ∆.F ϕ = ψ
cF : ∆.F ϕ ∼ ψ ∈ η/C
newtype ∆.M a = MkM ϕ
M : ∆.?, MkM : ∆.M a ∼ ϕ ∈ ?/T
T HEOREM 21 (Local diamond property). If Good Γ and Γ `
ϕ1 ∈ κ and Γ ` ϕ
ϕ2 ∈ κ then there exists a ϕ3
such that Γ ` ϕ1
ϕ3 ∈ κ and Γ ` ϕ2
ϕ3 ∈ κ.
In addition, uses of the data constructor of a newtype, both in
terms and patterns, are translated to a use of the corresponding coercion in the obvious way. The important parts of these definitions are
that (i) type families accept code arguments, (ii) type instances give
rise to code equalities, and (iii) newtype definitions give rise to type
equality axioms. The bindings generated in this way can be easily
checked for well-formedness. If, in addition, the resulting context is
Good (see Section 4.2)—the only possible problem is the potential to generate overlapping type instance declarations—then
the context is consistent, which in turn guarantees type safety.
Notice that the source language features in this translation have
been already annotated with their kinds. This is a reasonable assumption. Prior to type inference, which translates a source declaration to an FC2 binding, we must have determined the kinds
involved in the declarations. For the purposes of this paper, we assume that the kinds are given—in practice they would be the output
of a kind inference process, potentially guided by the user to disambiguate role information and higher-order kinds.
For this to be a Good axiom set, the kind of F must be ?/C → ?
because the newtype Age is irreducible only in role C.
In the rest of this section, we sketch the proof that our conditions
are sufficient for consistency. Soundness is a straightforward proof.
T HEOREM 19 (Soundness). If if Good Γ and Γ ` ϕ1
κ then there is some γ such that Γ ` γ : ϕ1 ∼ ϕ2 ∈ κ.
ϕ2 ∈
The three key lemmas of the completeness proof are that joinability
is preserved under application and substitution and a local diamond
property of rewriting.
L EMMA 20 (Application). If Good Γ and Γ ` ϕ1 ⇔ ϕ01 ∈
(η1 /R1 → η2 )/R2 and Γ ` ϕ2 ⇔ ϕ02 ∈ η1 /min(R1 , R2 )
then Γ ` ϕ1 ϕ2 ⇔ ϕ01 ϕ02 ∈ η2 /R2 .
Newtype deriving Generative type abstraction is achieved in
Haskell using the newtype deriving mechanism, which allows type classes to be automatically lifted to new types. For instance, we may write
respect to codes (we use metavariable N newtypes):
Γ ` γ : N ϕ ∼ N ψ ∈ η/C
N :κ → η ∈ Γ
η 0 /R0 = nth k κ
Γ ` nth k γ : nth k ϕ ∼ nth k ψ ∈ η 0 /R
newtype Age = MkAge Int deriving Eq
The type class Eq a is a standard class in Haskell (signature, in
ML terminology) that defines one method, eq :: a → a → Bool.
For the type checker, a type class is nothing but a record type
containing the method eq. The deriving line automatically
generates an implementation of Eq Age from a pre-existing instance Eq Int. This can be done by simply applying a coercion
Eq Int ∼ Eq Age to the old record. It is straightforward to construct this coercion: lift the Age ∼ Int axiom from the newtype
definition over the Eq constructor and apply symmetry. Importantly, this lifting is safe because Eq has a parametric kind of the
form ?/T → ?. However, if a type class has a indexed kind of the
form ?/C → ?, newtype deriving is no longer sound. For
class C x where
op :: F x → x
instance C Int where ...
newtype Age = MkAge Int deriving C
It is unsound to lift the C Int implementation to a C Age, precisely because x has role C. The documented GHC bug mentioned
earlier arises precisely as the result of such an unsound lifting over
a non-parametric type class.
Relaxing decomposition
Recall the coercion decomposition rule, CN TH T, from Figure 4.
This rule allows us to deconstruct an equality of the form Γ ` γ :
T ϕ ∼ T ψ ∈ η/T. In effect, it asserts that data constructors are
injective. The rule is important because it is used in the operational
semantics to ensure subject reduction. However, the decomposition rule may be somewhat restrictive for some Haskell source programs. Consider the following:
The only subtle part of this rule is that R is not related to R0 , since
the equality Γ ` γ : N ϕ ∼ N ψ ∈ η/C is a C-equality (and
min(C, R0 ) = C).
Arguably, decomposition for injective type functions is also desirable, were we able to effectively specify and check that property.
Other technical differences of FC2 from System FC
The intermediate language FC2 described in this paper is a significant modification of System FC [Sulzmann et al. 2007] due to
the introduction of codes. However, FC2 also makes a number of
technical simplifications:
• The original System FC presentation includes coercion kinds,
σ1 ∼ σ2 . The original coercion language includes three additional constructs, one to coerce coercions, and two more to
decompose coercion kinds. By treating (ϕ1 ∼ ϕ2 ) ⇒ ϕ3 as
the application of a constructor (∼η ) we no longer need any
of these constructs in the operational semantics, nor have we
identified any uses of these constructs that are not encodable.
• The operational semantics rules of FC2 in Figure 5 not only
use simpler coercion constructs, but are also expressed without
need for substitutions, contrary to their original FC versions.
• FC2 replaces the FC coercions left and right, which decom-
posed arbitrary type applications, by nth, which decomposes
only the application of a datatype constructor. This allows us
to lift a tiresome restriction in in System FC, namely that type
functions were required to be saturated. Type family saturation
was necessary in FC, in order to prevent the decomposition of
equalities as F a ∼ Maybe [a] via left or right. Allowing unsaturated functions increases the expressiveness of FC2 , because
we can now abstract over type functoins, and also opens new
directions for future research on type inference in the presence
of unsaturated type functions.
• However, using nth instead of left and right does carry a
small price. Generalizing the example from Section 6.1, should
this program be well typed?
newtype M a = MkM (Maybe [a])
data Eq a b where EQ :: Eq a a
f :: Eq (M a) (M b) → a → b
f EQ x = x
data Eq a b where EQ :: Eq a a
f :: Eq (p q) (r s) → q → s
f EQ x = x
Pattern matching against the EQ constructor introduces a coercion
between M a and M b, which cannot be decomposed using the
CN TH T rule to obtain a ∼ b, so this program cannot be typed.
Nevertheless, we know that M is injective, because M a is defined
to be equal to Maybe [a], which is clearly injective.
On the other hand, the following newtype is not injective.
type instance G a = Char
newtype N a = MkN (G a)
To type it we must decompose a proof that p q ∼ r s to get a
proof that q ∼ s, which right could do, but nth cannot.
Some other differences are presentational:
• System FC used a common syntax for types and coercions,
which is a convenient pun, but has turned out to be more confusing than helpful. In FC2 we use a distinct syntax for types
and coercions (Figure 1).
• In FC2 we define top-level axiom schemes c : ∆. ϕ1 ∼ ϕ2 /R
Here, it is possible to derive Γ ` γ : N Int ∼ N Char ∈ ?/T,
using the axiom for G, even though Int is not coercible to Char.
It turns out that newtype definitions are always injective with
respect to code equality, but they might not be injective with respect to type equality (as illustrated by the two examples above).
Thus it would be sound and potentially useful (but not necessary
for type soundness) to introduce yet another decomposition rule
for newtype definitions that takes advantage of injectivity with
directly, and fully instantiate them at every occurrence with
the form c γ (Figure 1). System FC instead defined a toplevel axiom scheme as an equality between polytypes, thus
c : ∀∆.ϕ1 ∼ ∀∆.ϕ2 . Here again FC is confusing (but not
wrong) so in FC2 we opt for telling the story more directly,
albeit with slightly more syntax. Moreover the kinding rules for
∀ (PA LL and CA LL) can insist that the body of the forall has
kind ? as is conventional.
• Using telescopes in the treatment of datatypes simplifies the
operational semantics rules but is also (only slightly) more
expressive: The types of data constructors do not have to have
their quantified variables preceding their coercion and term
arguments. Instead, telescopes allow arbitrary interleavings.
Related work
Previous work on System FC [Sulzmann et al. 2007] discusses a
significant amount of related work, in typed languages with explicit proof witnesses [Licata and Harper 2005; Shao et al. 2005], or
in calculi that support coercions [Breazu-Tannen et al. 1991]. Below, we present related work in generativity and abstraction, typeindexed constructs and the separation between codes and types.
Generativity, abstraction, and module systems Generativity and
abstraction has been studied extensively in the context of ML module systems [Milner et al. 1997]. Russo shows how generativity
in module systems is connected to existential quantification [Russo
1999] and Dreyer [2005] has studied this connection in the presence
of recursive modules. Montagu and Rémy [2009] refine this connection by introducing “open” existential types. Rossberg [2008]
uses flexible generativity to explain ML-style module sealing.
Type abstraction can be understood in terms of dynamic name
generation [Rossberg 2003; Vytiniotis et al. 2005], which can reestablish abstraction properties in languages with dynamic type
analysis. Neis et al. [2009] prove a parametricity theorem in this
setting. In addition, they use a translation from polymorphism to
generative types to establish the parametric behavior of certain
functions although they work in a non-parametric language.
Although many of these languages support type generativity
and non-parametric features, they do not exhibit the soundness
problems described in the paper, mainly due to the absence of typelevel type dispatching. Nevertheless, the techniques developed in
the aforementioned related work would be valuable in the formal
study of the parametricity properties of FC2 .
Type-indexed types Although many systems for generic programming support dynamic computation based on types, very few
systems allow the structure of types to be destructed to produce
other types. However, such facility is often necessary to describe
the type of generic programs. For example, Harper and Morrisett
include a Typerec operator to their typed intermediate language
[Harper and Morrisett 1995], to describe type-directed optiλM
mizations. (They credit NuPRL’s mechanism of “Universe Elimination” in NuPRL as the inspiration for this operation [Constable
1982; Constable and Zlatin 1984].)
To support generic programming in source languages, Hinze,
Jeuring and Löh added Type-Indexed Datatypes [Hinze et al.
2002] to the Generic Haskell front end. In later work, Chakravarty
et al. [2005b] introduced associated data families in GHC, which
are type-indexed datatypes associated with type class instances.
Extending this work, they later introduced associated type synonyms [Chakravarty et al. 2005a], which are proper type-level functions with instances associated with type class instances. Currently,
the source language of GHC also supports standalone type-level
type functions, often referred to as indexed type families [Kiselyov et al. 2010; Schrijvers et al. 2008], a feature that we have
extensively used in our presentation.
to a new code, inhabiting an open universe of codes, and whose
interpretation coincides with the interpretation of its definition.
Languages based on dependent type theory, such as Agda [Bove
et al. 2009] or Coq [The Coq Team], naturally offer type-level computation to construct types, but they allow elimination of codes
only, not types. Therefore, they do not exhibit the same soundness
problem, as the expressiveness of these languages can readily enforce the distinction between types and codes. The disadvantage
is the extra programming verbosity of explicit definitions and interpretations of codes. To better support generic programming, the
dependently-typed language Epigram [Chapman et al. 2010] identifies types with their code universes.
The LX language [Crary and Weirich 1999] also uses universe
constructions to solve problems with type-directed compilation.
When the type translation in a compiler pass is not the identity
then type dispatch must be compiled to code dispatch (so the generated code can dispatch on source types instead of target types).
The interpretation of codes is then the type translation. To support universes, LX includes datakinds (for codes) and primitive recursive functions over datakinds (for their interpretation at types).
In LX, source types Age and Int would be mapped to definable
codes AgeCode and IntCode, and would be accompanied by
an interpretation function such that interp(AgeCode) equals
Int and interp(IntCode) equals Int. Therefore, the problem with generativity would not show up in that context. If one
wanted to solve the problem in this paper along the LX lines, one
would have to translate source Haskell types to void types that stand
in as codes and handle the interp() function as any other type
function. This function, as well as interpreting the codes as types,
would have to be accompanied with suitable congruence axioms,
like interp(T t) ∼ T (interp(t)). Explicitly introducing these axioms means that coercions would be significantly more
verbose. Our system dispenses with an explicit interp() function by conveniently using the roles in the judgements to determine
whether we wish to derive an equality between codes or between
their interpretations.
Conclusions and future work
In this paper we have identified a problem for the safe interaction of
flexible type generativity and type-level computation. We have proposed a solution that distinguishes between indexed and parametric
type contexts, by extending the language of kinds, and formalized
the solution in the FC2 language. We have several avenues for future research in mind, which we outline below.
Source language technology We would like to work on ways
to expose the FC2 expressive features to programmers. Specific
directions are: enriching the kind declarations with the ability to
declare parametric or indexed type-level constructs, introducing
type family injectivity annotations, extending kind inference with
roles, and extending type inference to support unsaturated functions
using the more sophisticated kinds.
Enriching the universes of codes with terms We are currently
working on enriching the universe of codes with constants or functions drawn from the term syntax, such as data constructors, in order to enable direct dependently-typed programming in Haskell.
Codes, types, and interpretations Our distinction between codes
and types—and our terminology—is inspired by similar notions in
intuitionistic type theory [Benke et al. 2003; Dybjer 2000; MartinLöf 1975]. There, types (sets) are constructed as the recursive
interpretation of codes, which inhabit inductively constructed code
universes. A newtype definition can be viewed as giving rise
More roles Lemma 6 asserts that the equivalence classes induced
by T-equality are refined by C-equality. However, our approach
readily extends to arbitrary lattices of roles with gradually more
refined equivalence classes as we move down the relation.
Consider, for example, a lattice with new roles C1 and C2 ,
each lying between C and T, but incomparable with each other.
These two roles model partial knowledge of newtype equalities.
For example, perhaps C1 identifies Age and Int but distinguishes
a different newtype Moo from its definition Bool. Conversely,
C2 can identify Moo and Bool and distinguish Age and Int.
These more precise roles could be used to give more precise types
to nonparametric functions by identifying exactly which newtype
equivalences they do and do not respect. However, we have not yet
explored the practical implications of this precision.
Furthermore, one could explore adding a role above T. Type
functions with kind ?/T → ?, are not necessarily parametric in
FC2 . For example, because G below does not match newtypes it
may be assigned either kind ?/T → ? or ?/C → ?.
M. Benke, P. Dybjer, and P. Jansson. Universes for generic programs and
proofs in dependent type theory. Nordic J. of Computing, 10(4):265–
289, 2003. ISSN 1236-6064.
A. Bove, P. Dybjer, and U. Norell. A brief overview of agda — a functional
language with dependent types. In TPHOLs ’09: Proceedings of the
22nd International Conference on Theorem Proving in Higher Order
Logics, pages 73–78, Berlin, Heidelberg, 2009. Springer-Verlag.
V. Breazu-Tannen, T. Coquand, C. A. Gunter, and A. Scedrov. Inheritance
as implicit coercion. Information and Computation, 93:172–221, 1991.
M. M. T. Chakravarty, G. Keller, and S. Peyton Jones. Associated type
synonyms. In ICFP ’05: Proceedings of the Tenth ACM SIGPLAN
International Conference on Functional Programming, pages 241–253,
New York, NY, USA, 2005a. ACM.
M. M. T. Chakravarty, G. Keller, S. Peyton Jones, and S. Marlow. Associated types with class. SIGPLAN Not., 40(1):1–13, 2005b. ISSN
J. Chapman, P. Évariste Dagand, C. McBride, and P. Morris. The gentle art
of levitation. In Proceedings of the Fifteenth ACM SIGPLAN International Conference on Functional Programming (ICFP ’10), Baltimore,
MD, USA, September 2010. To appear.
J. Cheney and R. Hinze. First-class phantom types. CUCIS TR2003-1901,
Cornell University, 2003.
R. L. Constable. Intensional analysis of functions and types. Technical
Report CSR-118-82, Department of Computer Science, University of
Edinburgh, June 1982.
R. L. Constable and D. R. Zlatin. The type theory of PL/CV3. ACM
Transactions on Programming Languages and Systems, 6(1):94–117,
Jan. 1984.
K. Crary and S. Weirich. Flexible type analysis. In Proceedings of the
Fourth ACM SIGPLAN International Conference on Functional Programming, pages 233–248, Paris, France, Sept. 1999.
N. G. de Bruijn. Telescopic mappings in typed lambda calculus. Inf.
Comput., 91(2):189–204, 1991. ISSN 0890-5401.
D. Dreyer. Recursive type generativity. In ICFP ’05: Proceedings of the
Tenth ACM SIGPLAN International Conference on Functional Programming, pages 41–53, New York, NY, USA, 2005. ACM.
P. Dybjer. A general formulation of simultaneous inductive-recursive definitions in type theory. Journal of Symbolic Logic, 65(2):525–549, 2000.
C. V. Hall, K. Hammond, S. L. Peyton Jones, and P. L. Wadler. Type classes
in Haskell. ACM Transactions on Programming Languages and Systems,
18(2):109–138, Mar. 1996.
R. Harper and G. Morrisett. Compiling polymorphism using intensional
type analysis. In POPL ’95: Proceedings of the 22nd ACM SIGPLANSIGACT symposium on Principles of programming languages, pages
130–141, New York, NY, USA, 1995. ACM. ISBN 0-89791-692-1.
R. Hinze, J. Jeuring, and A. Löh. Type-indexed data types. In B. M.
Eerke Boiten, editor, Proceedings of the Sixth International Conference
on Mathematics of Program Construction (MPC 2002), pages 148–174,
Dagstuhl, Germany, July 2002.
O. Kiselyov, S. Peyton Jones, and C. Shan. Fun with type functions.
Springer, 2010.
D. R. Licata and R. Harper. A formulation of dependent ML with explicit
equality proofs. Technical Report CMU-CS-05-178, Carnegie Mellon
University Department of Computer Science, 2005.
P. Martin-Löf. An intuitionistic theory of types: Predicative part. In Proceedings of the Logic Colloquium, 1973, volume 80 of Studies in Logic
and the Foundations of Mathematics, pages 73–118. North-Holland,
R. Milner, M. Tofte, and D. Macqueen. The Definition of Standard ML.
MIT Press, Cambridge, MA, USA, 1997. ISBN 0262631814.
B. Montagu and D. Rémy. Modeling abstract types in modules with
open existential types. In POPL ’09: Proceedings of the 36th annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, pages 354–365, New York, NY, USA, 2009. ACM.
G. Neis, D. Dreyer, and A. Rossberg. Non-parametric parametricity. In
ICFP ’09: Proceedings of the 14th ACM SIGPLAN International Conference on Functional Programming, pages 135–148, New York, NY,
USA, 2009. ACM. ISBN 978-1-60558-332-7.
S. Peyton Jones et al. The Haskell 98 language and libraries: The revised
report. Journal of Functional Programming, 13(1):0–255, Jan 2003.
S. L. Peyton Jones, D. Vytiniotis, S. Weirich, and G. Washburn. Simple
unification-based type inference for GADTs. In International Conference on Functional Programming (ICFP), Portland, OR, USA, Sept.
B. C. Pierce, editor. Advanced Topics in Types and Programming Languages. MIT Press, 2005.
A. Rossberg. Dynamic translucency with abstraction kinds and higher-order
coercions. Electr. Notes Theor. Comput. Sci., 218:313–336, 2008.
A. Rossberg. Generativity and dynamic opacity for abstract types. In PPDP
’03: Proceedings of the 5th ACM SIGPLAN International Conference on
Principles and Practice of Declaritive Programming, pages 241–252,
New York, NY, USA, 2003. ACM. ISBN 1-58113-705-2.
C. V. Russo. Non-dependent types for Standard ML modules. In PPDP ’99:
Proceedings of the International Conference PPDP’99 on Principles
and Practice of Declarative Programming, pages 80–97, London, UK,
1999. Springer-Verlag. ISBN 3-540-66540-4.
T. Schrijvers, S. Peyton Jones, M. Chakravarty, and M. Sulzmann. Type
checking with open type functions. In ICFP ’08: Proceeding of the 13th
ACM SIGPLAN international conference on Functional programming,
pages 51–62, New York, NY, USA, 2008. ACM.
Z. Shao, V. Trifonov, B. Saha, and N. Papaspyrou. A type system for
certified binaries. ACM Trans. Program. Lang. Syst., 27(1):1–45, 2005.
M. Sulzmann, M. M. T. Chakravarty, S. Peyton Jones, and K. Donnelly.
System F with type equality coercions. In TLDI ’07: Proceedings of the
2007 ACM SIGPLAN International Workshop on Types in Languages
Design and Implementation, pages 53–66, New York, NY, USA, 2007.
The Coq Team. Coq. URL
D. Vytiniotis, G. Washburn, and S. Weirich. An open and shut typecase. In
ACM SIGPLAN Workshop in Types in Language Design and Implementation, Long Beach, CA, USA, Jan. 2005.
H. Xi, C. Chen, and G. Chen. Guarded recursive datatype constructors. In
POPL, pages 224–235, 2003.
axG1 : G Int ∼ Bool/C
axG2 : G Bool ∼ Char/C
To distinguish truly parametric functions from those like G, we
could add a role P, induced by the relation that equates all types
of the same kind. As this equivalence is coarser than T, it could not
(and should not) participate in coercions. However, this new role
would provide a yet more descriptive kind for type functions.
We thank Brent Yorgey and the attendees of the Type System
Wrestling sessions at MSR Cambridge for many useful discussions.
We wrote this paper using the Ott tool. This work was partially
supported by grants from DARPA (CSSG) and NSF (0910786) and
a Sloan Foundation fellowship.
Additional specification
For space reasons, the following definitions and rules did not appear in the main text of the paper.
D EFINITION 25. We define ∀ ∆.σ by induction on ∆ as follows:
∀ · .σ
∀ (a:κ, ∆).σ
∀a : κ.(∀ ∆.σ)
D EFINITION 26 (Multilifting Operation). Define the multiple lifting operation, written ϕ[a 7→ γ], by induction on ϕ.
a[a 7→ γ]
b[a 7→ γ]
H [a 7→ γ]
(ϕ ψ)[a 7→ γ]
(∀b : κ.σ)[a 7→ γ]
γi when ai in a
b when b 6∈ a
(ϕ[a 7→ γ]) (ψ[a 7→ γ])
∀b : κ.(σ[a 7→ γ])
Γ ` {bnd }
Γ ` {a:κ}
Γ ` {(→): ? /T → ?/T → ?}
Γ ` {(∼η ):η/C → η/C → ?/T → ?}
Γ ` {T :η}
Γ ` {F :η}
Γ, ∆ ` ϕ1 : η/R Γ, ∆ ` ϕ2 : η/R
Γ ` {c : ∆. ϕ1 ∼ ϕ2 /R}
Γ ` σ : ?/T
Γ ` {x :σ}
Γ ` σ : ?/T
Γ ` {K :σ}
dom bnd # dom Γ Γ ` {bnd }
` Γ, bnd
γ0 = sym (nth 0 γ) γ1 = nth 1 γ
((λx :σ1 .e1 ) . γ) e2
(λx :σ1 .(e1 . γ1 )) (e2 . γ0 )
(λx :σ.e1 ) e2
e1 e2
e1 [x 7→ e2 ]
e10 e2
Σ ` γ : ∀a : κ.σ1 ∼ ∀a : κ.σ2 ∈ ?/T
((Λa:κ.e) . γ) ϕ
(Λa:κ.(e . γ@a)) ϕ
(Λa:κ.e) ϕ
e1 ϕ
e[a 7→ ϕ]
e10 ϕ
Σ ` γ : (∼η ) ϕ1 ϕ2 σ ∼ (∼η ) ϕ01 ϕ02 σ 0 ∈ ?/T
γ0 = nth 0 γ γ1 = sym (nth 1 γ) γ2 = nth 2 γ
((Λc : ϕ1 ∼ ϕ2 .e) . γ) γ 0
(Λc : ϕ1 ∼ ϕ2 .(e . γ2 )) (γ0 ; γ 0 ; γ1 )
(Λc : ϕ1 ∼ ϕ2 .e) γ
e10 γ
e1 γ
(v . γ1 ) . γ2
e .γ
e[c 7→ γ]
v . (γ1 ; γ2 )
e0 . γ
Σ ` γ : T ϕ ∼ T ϕ0 ∈ ?/T
K :∀ a : κ.σ 0 ∈ Σ
caseσ (K ϕ ρ) . γ of brs
caseσ K ϕ0 (ρ . σ 0 [a 7→ nth γ]) of brs
1≤j ≤n
caseσ (Kj ϕ ρ) of Ki ⇒ ei
caseσ e of brs
Additional lemmas and proofs
Preservation and progress
ej ρ
caseσ e 0 of brs
We state additional basic properties of our judgements below. Proofs of these properties are by straightforward induction.
L EMMA 27 (Type regularity). If Γ ` e : σ then Γ ` σ : ?/T and ` Γ.
L EMMA 28 (Type substitution). Say Γ1 ` ϕ0 : κ0 .
If Γ1 , a:κ0 , Γ2
If Γ1 , a:κ0 , Γ2
If Γ1 , a:κ0 , Γ2
If Γ1 , a:κ0 , Γ2
If Γ1 , a:κ0 , Γ2
` ϕ : κ then Γ1 , (Γ2 [a 7→ ϕ0 ]) ` ϕ[a 7→ ϕ0 ] : κ
` ϕ : κ then Γ1 , (Γ2 [a 7→ ϕ0 ]) ` ϕ[a 7→ ϕ0 ] : κ
` γ : ϕ1 ∼ ϕ2 ∈ κ then Γ1 , (Γ2 [a 7→ ϕ0 ]) ` γ[a 7→ ϕ0 ] : ϕ1 [a 7→ ϕ0 ] ∼ ϕ2 [a 7→ ϕ0 ] ∈ κ.
` γ : ϕ1 ∼ ϕ2 ∈ κ then Γ1 , (Γ2 [a 7→ ϕ0 ]) ` γ[a 7→ ϕ0 ] : ϕ1 [a 7→ ϕ0 ] ∼ ϕ2 [a 7→ ϕ0 ] ∈ κ.
` e : σ then Γ1 , (Γ2 [a 7→ ϕ0 ]) ` e[a 7→ ϕ0 ] : σ[a 7→ ϕ0 ]
L EMMA 29 (Coercion substitution). Say Γ1 ` γ : ϕ1 ∼ ϕ2 ∈ η/R.
1. If Γ1 , c:ϕ1 ∼ ϕ2 /R, Γ2 ` γ 0 : ψ1 ∼ ψ2 ∈ κ0 then Γ1 , Γ2 ` γ 0 [c 7→ γ] : ψ1 ∼ ψ2 ∈ κ0
2. If Γ1 , c:ϕ1 ∼ ϕ2 /R, Γ2 ` γ : ψ 1 ∼ ψ 2 ∈ κ then Γ1 , Γ2 ` γ[c 7→ γ] : ψ 1 ∼ ψ 2 ∈ κ
3. If Γ1 , c:ϕ1 ∼ ϕ2 /R, Γ2 ` e : σ then Γ1 , Γ2 ` e[c 7→ γ] : σ
L EMMA 30 (Term substitution). Say Γ1 ` e 0 : σ 0 . If Γ1 , x :σ 0 , Γ2 ` e : σ, then Γ1 , Γ2 ` e[x 7→ e 0 ] : σ.
L EMMA 31 (Multilifting). If Γ, ∆ ` σ : κ and Γ ` γi : ϕi ∼ ϕ0i ∈ κi for each ai :κi in ∆, then Γ ` σ[a 7→ γ] : σ[a 7→ ϕ] ∼ σ[a 7→
ϕ0 ] ∈ κ
Next we sketch the proofs of the progress and preservation theorems.
P ROOF OF T HEOREM 12 (Preservation): If Γ ` e1 : σ and e1
e2 then Γ ` e2 : σ.
Proof by induction on e1
e2 .
• Case SSB ETA, SSTB ETA, SSCB ETA: Application of the appropriate substitution lemma.
• Case SSC ASE M ATCH: By inversion Γ ` Kj ϕ ρ : T ϕ and Kj :∀ ∆.ψj ∈ Γ and ψj [a 7→ ϕ] = ∀ Θj .T ϕ and Γ ` ej : ∀ Θj .σ. Further
inversion gives Γ ` ρ : Θj By repeated use of the application rule, Γ ` ej ρ : σ.
• Case SSP USH: By inversion σ = σ20 and Γ ` γ : (σ1 → σ10 ) ∼ (σ2 → σ20 ) ∈ ?/T and Γ ` (λx :σ1 .e1 ) : σ1 → σ10 and Γ ` e2 : σ2 . We
have Γ ` γ0 : σ2 ∼ σ1 ∈ ?/T and Γ ` γ1 : σ10 ∼ σ20 ∈ ?/T. Therefore, Γ ` (λx :σ1 .(e1 . γ1 )) : σ1 → σ20 and Γ ` e2 . γ0 : σ1 and the
result follows by the application typing rule.
Case SSTP USH: Straightforward use of inversion and typing/coercion rules.
Case SSCP USH: By inversion, Γ ` γ : (ϕ1 ∼ ϕ2 ) ⇒ σ ∼ (ϕ01 ∼ ϕ02 ) ⇒ σ 0 ∈ ?/T and Γ ` γ 0 : ϕ01 ∼ ϕ02 ∈ η/C and
Γ, c:ϕ1 ∼ ϕ2 /C ` e : σ. We have Γ ` γ0 : ϕ1 ∼ ϕ01 ∈ η/C and Γ ` γ1 : ϕ02 ∼ ϕ2 ∈ η/C and Γ ` γ2 : σ ∼ σ 0 ∈ ?/T. Therefore
Γ ` e . γ2 : σ 0 and Γ ` γ0 ; γ 0 ; γ1 : ϕ1 ∼ ϕ2 ∈ η/C. Finally, the RHS has type σ 0 by coercion abstraction and application typing rules.
Case SSKP USH: By inversion, Γ ` γ : T ϕ ∼ T ϕ0 ∈ ?/T and K :∀ ∆.σ 0 ∈ Σ and Γ ` K ϕ ρ . γ : T ϕ0 . Further inversion
yields Γ ` K ϕ ρ : T ϕ and Γ ` ρ : Θ[a 7→ ϕ] where σ 0 = ∀ Θ.T a. Let γ = (∀ Θ.T a)[a 7→ nth γ] By lifting lemma,
Γ ` γ : ∀ Θ[a 7→ ϕ].T ϕ ∼ ∀ Θ[a 7→ ϕ0 ].T ϕ0 ∈ ?/T. Therefore we have by Lemma 11 that Γ ` ρ . γ : Θ[a 7→ ϕ0 ] and thus
Γ ` K ϕ0 (ρ . γ) : T ϕ0 .
Case T RANS: Application of CT RANS.
All other cases by induction.
P ROOF OF T HEOREM 15 (Progress): If Σ is consistent and Σ ` e1 : σ and e1 is not a value v or a coerced value v . γ, then there exists an
e2 such that e1
e2 .
Proof by induction on e1 . Assume e1 is not a value or a coerced value.
• Case e1 = e e 0 . By induction, either e is a value v or a coerced value v . γ or takes a step. In the first case, by canonical forms, v is either
an abstraction (which beta reduces) or a constructor application (which means that e1 is a value). In the second case, we have a coercion
γ between a value type τ (the type of v ) and σ1 → σ2 . By consistency, then τ must be σ10 → σ20 and the push rule applies. In the last
case e1 steps by the application congruence rule.
• Case e1 = e ϕ and e1 = e γ are analogous to the previous case.
• Case e1 = e . γ. By induction, either e is value v or a coerced value v . γ 0 or takes a step. In the first case, then e1 is a coerced value. In
the second case, (v . γ 0 ) . γ steps to v . (γ 0 ; γ). In the last case, the congruence rule for coercion apply.
• Case e1 = caseσ e of brs. By induction, either e is a value v or a coerced value v . γ or takes a step. In the first case, by canonical
forms, v is a a constructor application, so the case expression reduces. In the second case, we have a coercion γ between a value type τ
(the type of v ) and T ϕ. By consistency, then τ must be T ϕ0 and the push rule applies. In the last case e1 steps by the case congruence
L EMMA 32 (Rewriting regularity). If Γ ` ϕ1
ϕ2 ∈ κ then ` Γ and Γ ` ϕ1 : κ and Γ ` ϕ2 : κ.
Once we get a type constants at the head, it remains.
ϕ0 ∈ η/R then ϕ0 = T ϕ0 and Γ ` ϕ
L EMMA 33 (Constant Rewriting). If Good Γ and Γ ` T ϕ
ϕ0 ∈ κ/R.
(Proof is by inspection of the rules of type rewriting.) There is a similar result for types of the form ∀a : κ.σ.
We can add types to the end of a list of arguments that reduce.
L EMMA 34 (Snoc). If Γ ` ϕ
ϕ0 ∈ κ/R1 and Γ ` ψ
ψ 0 ∈ η/min(R1 , R2 ), then Γ ` ϕ, ψ
ϕ0 , ψ 0 ∈ (κ, η/R2 )/R1
ϕ01 ∈ (η1 /R1 → η2 )/R2 and Γ ` ϕ2
L EMMA 35 (Single Rewriting Application). If Good Γ and Γ ` ϕ1
then Γ ` ϕ1 ϕ2
ϕ01 ϕ02 ∈ η2 /R2 .
ϕ02 ∈ η1 /min(R1 , R2 )
Proof Straightforward case analysis on the rewriting of ϕ1 .
L EMMA 36 (Matching). If Good Γ and Γ ` c ψ : H ϕ ∼ σ ∈ η/R and Γ ` ϕ
Γ ` c ψ : H ϕ0 ∼ σ 0 ∈ η/R and Γ ` σ
σ 0 ∈ η/R.
ϕ0 ∈ κ1 /R then there exist ψ and σ 0 , such that
Proof Follows by the definition of Good Γ (first condition).
P ROOF OF T HEOREM 21: If Good Γ and Γ ` ϕ
and Γ ` ϕ2
ϕ3 ∈ κ.
ϕ1 ∈ κ and Γ ` ϕ
ϕ2 ∈ κ then there exists a ϕ3 such that Γ ` ϕ1
ϕ3 ∈ κ
Proof We prove this by induction on the size of ϕ. We proceed by inversion on the rules used to deduce that Γ ` ϕ
ϕ1 ∈ κ and
ϕ2 ∈ κ. Most of the cases are straightforward, and follow by the induction hypothesis, except for two combinations:
H ϕ0 ∈ η/R given that
• Case RC ONST-RR ED. In this case we have that Γ ` H ϕ
ϕ0 ∈ κ/R
Moreover, we let ϕ = ϕ1 , ϕ2 such that Γ ` H ϕ
∈ η/R given that
ϕ001 ∈ κ1 /R
ϕ002 ∈ κ2 /R
Γ ` c ψ : H ϕ001 ∼ σ ∈ (κ2 → η)/R
Γ ` ϕ1
Γ ` ϕ2
By splitting ϕ accordingly, let us name the split ϕ01 , ϕ02 , it must then be:
ϕ01 ∈ κ1 /R
ϕ02 ∈ κ2 /R
Γ ` ϕ1
Γ ` ϕ2
By using (2) with (5), and (3) with (5), and applying the induction hypothesis to each element in the corresponding vectors we get
Γ ` ϕ01
Γ ` ϕ001
Γ ` ϕ02
Γ ` ϕ002
κ1 /R
κ1 /R
κ2 /R
κ2 /R
for some vectors ϕ11 and ϕ22 .
We will be done if we can find some σ 0 for which we can show the following two statements:
Γ ` σ ϕ002
Γ ` H ϕ01 ϕ02
σ 0 ϕ22 ∈ η/R
σ 0 ϕ22 ∈ η/R
For this, we will appeal to the matching lemma, and equations (4), (8). This lemma gives ψ and and σ 0 such that:
σ 0 ∈ (κ2 → η)/R
Γ ` c ψ : H ϕ11 ∼ σ 0 ∈ (κ2 → η)/R
By appealing to (a straightforward extension of) the single application lemma, and using (13) and (9) we get our first goal, (11). For the
second goal (12) we use (7), (9), (14), and apply rule RR ED. Hence, this case is done.
• Case RRED-RRED. In this case we let ϕ = ϕ1 , ϕ2 such that Γ ` H ϕ
σ ϕ02 ∈ η/R given that
ϕ01 ∈ κ1 /R
ϕ02 ∈ κ2 /R
Γ ` c ψ : H ϕ01 ∼ σ ∈ (κ2 → η)/R
Γ ` ϕ1
Γ ` ϕ2
σ0 ϕ04
We let another possible split ϕ = ϕ3 , ϕ4 such that Γ ` H ϕ
∈ η/R given that
Γ ` ϕ3
Γ ` ϕ4
ϕ03 ∈ κ3 /R
ϕ04 ∈ κ4 /R
Γ ` c 0 ψ : H ϕ03 ∼ σ0 ∈ (κ4 → η)/R
By the induction hypothesis for every type in ϕ, and (15), (16), (18), (19) we get that there exists a ϕ such that
Γ ` (ϕ03 , ϕ04 )
Γ ` (ϕ01 , ϕ02 )
ϕ00 ∈ κ/R
ϕ00 ∈ κ/R
where κ = κ1 , κ2 = κ3 , κ4 . Now, consider the 1 − 2 split and 3 − 4 split induced on ϕ00 = ϕ001 , ϕ002 = ϕ003 , ϕ004 . By the matching lemma,
and (20) we know that there exist ψ such that Γ ` c ψ : H ϕ001 ∼ σ 00 ∈ (κ2 → η)/R and moreover Γ ` σ
σ 00 ∈ (κ2 → η)/R.
0 000
Similarly, by the matching lemma and (21) we know that there exist ψ such that Γ ` c ψ : H ϕ3 ∼ σ0 ∈ (κ4 → η)/R with
Γ ` σ0
σ000 ∈ (κ4 → η)/R. However, ϕ003 and ϕ001 come from the same vector ϕ00 and hence, by appealing to the definition of
good contexts, it must be the case that the 1 − 2, 3 − 4 split is exactly the same. Hence ϕ003 = ϕ001 and similarly ϕ002 = ϕ004 , c = c 0 ,
σ 00 = σ000 = σ 000 . I.e. we have:
Γ ` σ0
σ 000 ∈ (κ2 → η)/R
σ 000 ∈ (κ4 → η)/R
σ 000 ϕ002 ∈ (κ2 → η)/R
σ 000 ϕ004 ∈ (κ4 → η)/R
Hence, it suffices to show the following two:
Γ ` σ ϕ02
Γ ` σ0 ϕ04
Since the 1 − 2 split and the 3 − 4 split are the same, the result will follow by the induction hypothesis for (15) with (18), and (16) with
(19), reduction (23) and (24), and the single application lemma.
P ROOF OF L EMMA 20: If Good Γ and Γ ` ϕ1 ⇔ ϕ01 ∈ (η1 /R1 → η2 )/R2 and Γ ` ϕ2 ⇔ ϕ02 ∈ η1 /min(R1 , R2 ) then
Γ ` ϕ1 ϕ2 ⇔ ϕ01 ϕ02 ∈ η2 /R2 .
Proof Assume the intermediate join points of ϕ1 and ϕ01 and ϕ2 and ϕ02 , call them σ1 and σ2 respectively. By applying multiple times the
single application lemma and using reflexivity of rewriting we see that σ1 σ2 must be a join point for ϕ1 ϕ2 and ϕ01 ϕ02 .
C OROLLARY 37 (Multi-Application). If Good Γ and Γ ` ϕ1 ⇔ ϕ01 ∈ (κ → η)/R and Γ ` ϕ2 ⇔ ϕ02 ∈ κ/R then Γ ` ϕ1 ϕ2 ⇔
ϕ01 ϕ02 ∈ η/R.
L EMMA 38 (StepSubst1). If Good Γ and Γ, a:κ, ∆ ` σ : η/R and Γ ` ϕ
ϕ0 ∈ κ, then Γ ` σ[a 7→ ϕ] ⇔ σ[a 7→ ϕ0 ] ∈ η/R.
Proof by induction on σ.
Case σ = F ϕ: By induction, we have Γ, ∆ ` ϕ[a 7→ ϕ] ⇔ ϕ[a 7→ ϕ0 ] ∈ κ1 /R. Result holds by the appliation lemma.
Case σ = b ϕ: By induction, we have Γ, ∆ ` ϕ[a 7→ ϕ] ⇔ ϕ[a 7→ ϕ0 ] ∈ κ1 /R. If b is not a, we are done by the application lemma.
Otherwise, suppose a = b: so we want to show that Γ, ∆ ` ϕ (ϕ[a 7→ ϕ]) ⇔ ϕ (ϕ[a 7→ ϕ0 ]) ∈ κ. This also holds by the application
Case σ = T ϕ: By induction and the application lemma.
Case σ = ∀b : κ.σ 0 : Result holds by induction.
L EMMA 39 (StepSubstMany). If Good Γ and Γ, a:κ, ∆ ` σ : κ0 and Γ ` ϕ
ϕ0 ∈ κ, then Γ ` σ[a 7→ ϕ] ⇔ σ[a 7→ ϕ0 ] ∈ κ0 .
Proof Proof is by induction on the number of steps in Γ ` ϕ
ϕ0 ∈ κ. If n = 0 then the result is trivial. Say n = m + 1 and
ϕ ∈ κ and Γ ` ϕ
ϕ ∈ κ in m steps. By induction, we have Γ ` σ[a 7→ ϕ00 ] ⇔ σ[a 7→ ϕ0 ] ∈ κ0 . We just need to show
that Γ ` σ[a 7→ ϕ] ⇔ σ[a 7→ ϕ00 ] ∈ κ0 . However, this result holds by lemma StepSubst1.
L EMMA 40 (StepSubst2). If Good Γ and Γ, a:κ, ∆ ` σ
σ 0 ∈ κ0 and Γ ` ϕ : κ, then Γ, ∆ ` σ[a 7→ ϕ] ⇔ σ[a 7→ ϕ0 ] ∈ κ0 .
Proof is by induction on σ, appealing to Application.
L EMMA 41 (Transitivity of rewriting). If Good Γ and Γ ` ϕ1 ⇔ ϕ2 ∈ κ and Γ ` ϕ2 ⇔ ϕ3 ∈ κ then Γ ` ϕ1 ⇔ ϕ3 ∈ κ.
Proof Assume that Γ ` ϕ1 ∗ σ ∈ κ and Γ ` ϕ2 ∗ σ ∈ κ in n1 and n2 steps respectively. Similarly, Γ ` ϕ2 ∗ σ 0 ∈ κ and
Γ ` ϕ3 ∗ σ 0 ∈ κ in n2 and n3 steps respectively. Because of reflexivity of rewritting we can extend all those reductions so that each
takes exactly m = max(n1 , n2 , n3 , n4 ) steps. The result then follows by the local diamond property.
P ROOF OF L EMMA 22 If Good Γ and Γ, a:κ, ∆ ` σ
σ 0 [a 7→ ϕ0 ] ∈ κ0 . :
σ 0 ∈ κ0 and Γ ` ϕ
ϕ0 ∈ κ, then there is some Γ, ∆ ` σ[a 7→ ϕ] ⇔
Proof is by induction on the number of steps in Γ, a:κ, ∆ ` σ
σ 0 ∈ κ0 . If n = 0, then the result follows from StepSubstMany.
If n = m + 1, suppose Γ, a:κ, ∆ ` σ
σ ∈ κ and Γ, a:κ, ∆ ` σ 00
σ 0 ∈ κ0 in m steps. By induction, we have
Γ, ∆ ` σ 00 [a 7→ ϕ] ⇔ σ 0 [a 7→ ϕ0 ] ∈ κ0 . We just need to show that Γ ` σ[a 7→ ϕ] ⇔ σ 00 [a 7→ ϕ] ∈ κ0 . However, this result
holds by lemma StepSubst2.
P ROOF OF L EMMA 23 If Good Γ and Γ ` γ : ϕ1 ∼ ϕ2 ∈ κ then Γ ` ϕ1 ⇔ ϕ2 ∈ κ. :
Proof is by induction on γ.
γ = ϕ. Trivial as ϕ1 = ϕ2 .
γ = γ1 γ2 . Holds by induction and application lemma above.
γ = ∀a : κ.γ 0 . Holds by induction and RA LL.
γ = c ψ where κ = η/R. We have ϕ1 = σ1 [a 7→ ψ] and ϕ2 = σ2 [a 7→ ψ]. We also must have Γ, ∆ ` σ1
σ2 ∈ η/R (by noting that
type variables are normal and instantiating c with variables in κ.) The desired result holds by the substitution lemmas and transitivity.
γ = sym γ 0 Trivial.
γ = γ1 ; γ2 Holds by transitivity of rewriting.
γ = nth k γ 0 By inversion we have Γ ` γ 0 : T ϕ1 ∼ T ϕ2 ∈ η/T. We want to show that there is some ϕ such that,
Γ ` nth k ϕ1 ∗ ϕ ∈ nth k κ and Γ ` nth k ϕ2 ∗ ϕ ∈ nth k κ.
By induction we have ϕ, such that Γ ` T ϕ1 ∗ ϕ ∈ η/T and Γ ` T ϕ2 ∗ ϕ ∈ η/T. By Constant Rewriting, we have ϕ = T ϕ0 ,
and Γ ` ϕ1 ∗ ϕ0 ∈ κ/T and Γ ` ϕ2 ∗ ϕ0 ∈ κ/T. By inversion of these two results, we get the appriate ϕ.
γ = γ1 @ψ By inversion we have Γ ` γ1 : ∀a : κ.σ1 ∼ ∀a : κ.σ2 ∈ ?/T and Γ ` ψ : κ. We want to show that there is some ϕ, such
that Γ ` σ1 [a 7→ ψ] ∗ ϕ ∈ ?/T and Γ ` σ2 [a 7→ ψ] ∗ ϕ ∈ ?/T. By induction, we know that there is some σ and ϕ0 , such that
Γ ` ∀a : κ.σ1 ∗ ∀a : κ.σ ∈ ?/T and Γ ` ∀a : κ.σ2 ∗ ∀a : κ.σ ∈ ?/T . The substitution lemma and transitivity gives us the desired
Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF