Notes on Hindley-Milner polymorphism


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.

The Hindley-Milner type system

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}$$ (+I1)
$$\frac{\Gamma \vdash e : u}{\Gamma \vdash \Inr t u e : t + u}$$ (+I2)
$$\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)

Auxiliary definitions

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*} $$

Equivalence of type schemes

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 $$

A syntax-directed variant

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}$$ (varS)
$$\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}$$ (letS)

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} $$