These notes summarize our development of Hindley-Milner polymorphism.
The key syntactic step that distinguishes this calculus is the separation of types (or ground types), which cannot contain quantifiers, and type schemes (or schematic types), which do. Types are defined as before, but with the addition of type variables.
Type | Meaning | Type | Meaning |
t * u |
Product types | Int |
Integers |
t + u |
Sum types | Bool |
Booleans |
t -> u |
Function types | 1 |
Unit type |
a |
Type variables |
Type schemes include types and the universal quantifier.
Scheme | Meaning |
t |
Trivial schemes |
∀a.t |
Universal quantification |
In practical languages, the quantifiers are usually left implicit. So in Haskell we write the
type of the length
function as [a] -> Integer
,
not ∀a.[a] -> Integer
. However, this is purely a matter of syntactic
convenience. We write the quantifiers explicitly here to make the role of the type rules clear.
Our first description of Hindley-Milner typing relies on introducing two new typing rules: one
for the introduction of quantifiers, and one for their elimination. We also discover the
term-level consequence of the stratification of types and schemes: only let
introduces polymorphism, while the remainder of the existing terms operate only on ground types.
Rule | Name | |
$$\frac{x : s \in \Gamma} {\Gamma \vdash x : s}$$ | (var) | |
$$\frac{\Gamma \vdash x : s \quad a \not\in fv(\Gamma)} {\Gamma \vdash x : \forall a. s}$$ | (∀I) | |
$$\frac{\Gamma \vdash x : \forall a. s} {\Gamma \vdash x : [a \mapsto t] s}$$ | (∀E) | |
$$\frac{\Gamma \vdash e_1 : s \quad \Gamma, x : s \vdash e_2 : t} {\Gamma \vdash \Let{x}{e_1}{e_2} : t}$$ | (let) | |
The following rules are unchanged from their simply typed presentations; however, note that they are restricted to apply to types, not to type schemes. | ||
$$\frac{\Gamma, x : t \vdash e : u}{\Gamma \vdash \mathtt{\backslash x \to e : t \to u}}$$ | (→I) | |
$$\frac{\Gamma \vdash e_1 : t \to u \quad \Gamma \vdash e_2 : t}{\Gamma \vdash e_1\,e_2 : u}$$ | (→E) | |
$$\frac{\Gamma \vdash e_1 : t_1 \quad \Gamma \vdash e_2 : t_2} {\Gamma \vdash (e_1, e_2) : t_1 * t_2}$$ | (×I) | |
$$\frac{\Gamma \vdash e_1 : t_1 * t_2 \quad \Gamma, x_1 : t_1, x_2 : t_2 \vdash e_2 : u} {\Gamma \vdash \Let{(x_1,x_2)}{e_1}{e_2} : u}$$ | (×E) | |
$$\frac{ }{\Gamma \vdash () : 1}$$ | (1I) | |
$$\frac{\Gamma \vdash e_1 : 1 \quad \Gamma \vdash e_2 : t} {\Gamma \vdash e_1; e_2 : t}$$ | (1E) | |
$$\frac{\Gamma \vdash e : t}{\Gamma \vdash \Inl t u e : t + u}$$ | (+I_{1}) | |
$$\frac{\Gamma \vdash e : u}{\Gamma \vdash \Inr t u e : t + u}$$ | (+I_{2}) | |
$$\frac{\Gamma \vdash e : t_1 + t_2 \quad \Gamma, x_1 : t_1 \vdash e_1 : u \quad \Gamma, x_2 : t_2 \vdash e_2 : u} {\Gamma \vdash \CCase e {x_1} {e_1} {x_2} {e_2} : u}$$ | (+E) | |
$$\frac{\Gamma \vdash e : (t \to u) \to (t \to u)} {\Gamma \vdash \mathtt{fix}\,e : t \to u}$$ | (fix) | |
The following rules are entirely unchanged. | ||
$$\frac{ }{\Gamma \vdash 1 : \Int}$$ | (const) | |
$$\frac{\Gamma \vdash e_1 : \Int \quad \Gamma \vdash e_2 : \Int}{\Gamma \vdash e_1 + e_2 : \Int}$$ | (const) | |
$$\frac{\Gamma \vdash e_1 : \Int \quad \Gamma \vdash e_2 : \Int}{\Gamma \vdash e_1 - e_2 : \Int}$$ | (const) | |
$$\frac{\Gamma \vdash e_1 : \Int \quad \Gamma \vdash e_2 : \Int}{\Gamma \vdash e_1 * e_2 : \Int}$$ | (const) | |
$$\frac{ }{\Gamma \vdash \mathtt{True} : \Bool}$$ | (const) | |
$$\frac{\Gamma \vdash e : \Int}{\Gamma \vdash \mathtt{isz}\,e : \Bool}$$ | (const) | |
$$\frac{\Gamma \vdash e_1 : \Bool \quad \Gamma \vdash e_2 : t \quad \Gamma \vdash e_3 : t} {\Gamma \vdash \If{e_1}{e_2}{e_3} : t}$$ | (if) |
The typing rules above make use of several auxiliary definitions—the (∀I) rule relies on knowing the free type variables of the typing environment, while the (∀E) rule uses substitutions on types. The definition of these ideas is routine; we present them here without further comment.
$$ \begin{align*} fv(\Int) = fv(\Bool) = fv(1) &= \emptyset & fv(t \times u) &= fv(t) \cup fv(u) \\ fv(a) &= \Set{a} & fv(t + u) &= fv(t) \cup fv(u) \\ fv(\forall a. t) &= fv(t) \setminus \Set a & fv(t \to u) &= fv(t) \cup fv(u) \end{align*} $$ $$ fv(\Gamma) = \bigcup \Set{fv(s) : (x : s) \in \Gamma} $$ $$ \begin{align*} [a \mapsto t] b &= \begin{cases} t &\text{if $a = b$} \\ b &\text{otherwise} \end{cases} & [a \mapsto t] (\forall b. u) &= \begin{cases} \forall b. u & \text{if $a = b$} \\ \forall b. [a \mapsto t]u & \text{otherwise} \end{cases} \\ [a \mapsto t]\Int &= \Int & [a \mapsto t](u_1 \times u_2) &= [a \mapsto t]u_1 \times [a \mapsto t]u_2 \\ [a \mapsto t]\Bool &= \Bool & [a \mapsto t](u_1 + u_2) &= [a \mapsto t]u_1 + [a \mapsto t]u_2 \\ [a \mapsto t]1 &= 1 & [a \mapsto t](u_1 \to u_2) &= [a \mapsto t]u_1 \to [a \mapsto t]u_2 \end{align*} $$
The syntax of type schemes distinguishes some types that might seem equivalent. For example,
the schemes ∀a. a -> a
and ∀b. b -> b
are
syntactically distinct, even though our intuition is that they describe the same terms.
Similarly, the types ∀a. ∀b. a -> b -> a
and ∀b.∀a. a -> b -> a
differ in the order of quantifiers,
which we might again expect to be insignificant.
We can demonstrate that these differences are acutally inconsequential, however. The following derivations are left as exercises.
Quantifier variables 1. Derive the following judgments.
$$ \vdash \backslash x \to x : \forall a. a \to a \qquad \vdash \backslash x \to x : \forall b. b \to b $$Quantifier variables 2. Derive the following judgment.
$$ \Set{id : \forall a. a \to a} \vdash id : \forall b. b \to b $$Quantifier ordering 1. Derive the following judgments.
$$ \vdash \backslash x \to \backslash y \to x : \forall a. \forall b. a \to b \to a \qquad \vdash \backslash x \to \backslash y \to x : \forall b. \forall a. a \to b \to a $$Quantifier ordering 2. Derive the following judgment.
$$ \Set{k : \forall a. \forall b. a \to b \to a} \vdash k : \forall b. \forall a. a \to b \to a $$We can also give a syntax-directed variant of the Hindley-Milner type system. The key observation is that, other than the (var) and (let) rules, the rules for the syntax forms all operation only on ground types. So, applications of (∀E) must occur immediately below the (var) rule, and applications of (∀I) can only usefully occur immediately above a (let) rule. Following this idea, we can introduce new versions of the variable and let rules that incorporate the uses of (∀E) and (∀I), as follows.
Rule | Name |
$$\frac{(x : s) \in \Gamma \quad t \in \gr s} {\Gamma \vdash x : t}$$ | (var^{S}) |
$$\frac{\Gamma \vdash e_1 : u \quad s = Gen(u, \Gamma) \quad \Gamma, x : s \vdash e_2 : t} {\Gamma \vdash \Let{x}{e_1}{e_2} : t}$$ | (let^{S}) |
Again, we make use of several auxiliary definitions. The generalization of $u$ with respect to $\Gamma$, written $Gen(u, \Gamma)$, is the most general type scheme that we can derive starting from $u$ by repeated applciations of the (∀I) rule. It is defined by
$$ Gen(u, \Gamma) = \forall a_1. \dots \forall a_n. u \quad \text{where $\Set{a_1, \dots, a_n} = fv(u) \setminus fv(\Gamma)$} $$The ground instance of $s$, written $\gr s$, is the set of ground types (i.e., non-schemes) that can be obtained by instantiating the quantifiers. That is, it is the set of types that can be obtained from $s$ by repeated applications of the (∀E) rule. It is defined as follows.
$$ \gr{\forall a. s} = \bigcup \Set{\gr{[a \mapsto t]s} : t \in Type} \qquad \gr t = \Set t $$The idea of the ground instances of a type scheme captures many of the intutions for type schemes that are not reflected directly in the syntax. For example, the following two properties are easy to prove.
$$ \gr{\forall a. \forall b. s} = \gr{\forall b. \forall a. s} \qquad \gr{\forall a. s} = \gr{\forall b. [a \mapsto b]s} $$