Inference of a principal type in the Hindley-Milner type system uses fresh type variables and type unification to satisfy the constraints imposed by expressions. A successful result describes every valid monomorphic instance through type substitution; generalization at suitable bindings forms a type scheme for later instantiation.
K combinator 2026-10-31
In lambda calculus, the closed lambda term discards its second argument. In combinatory logic, its primitive constant satisfies . The corresponding principal type in the simply typed lambda calculus is .
Apply a function to each element of a collection while retaining its arrangement. On lists, map f [] = [] and map f (x:xs) = f x : map f xs. Its principal type in Haskell is (a -> b) -> [a] -> [b]. The element types may change independently; no equality test or ordering of elements is required.
Both uses of x in this definition have the same monomorphic type within the body. Write x :: u -> v. The inner application x z forces z :: u and has type v; the outer application x (x z) forces v = u. Thus x :: u -> u. Finally, x (x z) is applied to y, so u must itself be a function type, say a -> b. This gives
f :: ((a -> b) -> a -> b) -> a -> (a -> b) -> b
f x y z = x (x z) y
The principal type is therefore
The argument x transforms a function into another function of the same type. The body transforms z twice, then applies the resulting function to y; no recursive type equation is required.
In the type signature map :: (u -> v) -> [u] -> [v], the supplied function is concat :: [[a]] -> [a]. Type inference therefore identifies u with [[a]] and v with [a]. Consequently,
map concat :: [[[a]]] -> [[a]]
Thus is the principal type. For example, map concat [[[1],[2,3]],[[4],[]]] gives [[1,2,3],[4]]: it concatenates each inner group, preserving the outermost grouping.
The principal types and definitions are:
map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr _ e []     = e
foldr f e (x:xs) = f x (foldr f e xs)

(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g x = f (g x)
The map function preserves the list shape while changing element type from a to b. The right fold replaces the empty list by e and each constructor by f, whose second argument and result therefore share the accumulator type b. Function composition requires the output type of g to agree with the input type of f. No Haskell type class constraints arise: these definitions never compare elements or perform arithmetic on them.
Take h = (.):
h :: (b -> c) -> (a -> b) -> a -> c
h f g x = f (g x)
This is its standalone principal type; in the right fold, c specializes to the type of a function from the accumulator to itself.
For fixed f, g and e, prove equality pointwise by structural induction on the list. At [] both sides return e. For x:xs, assume the equality for xs and calculate:
foldr f e (map g (x:xs))
  = f (g x) (foldr f e (map g xs))
  = f (g x) (foldr (h f g) e xs)
  = h f g x (foldr (h f g) e xs)
  = foldr (h f g) e (x:xs)
The second equality is the induction hypothesis. Function extensionality gives the required equality of composed functions. This is an instance of the right-fold fusion law.
For the full lazy Haskell interpretation, include a bottom value for an undefined list spine: both sides are bottom there. The same constructor argument covers every finite approximation with an undefined tail. The Scott continuity of the list operations then extends equality to their limits, including infinite lists. Ordinary induction on finite total lists alone proves only that restricted case.
The expression parses as g . (g x). Initially write g :: a -> r and x :: a. In the function composition, g x must itself be a function whose result can be passed to g. Hence r = b -> a for a fresh type b, so
g :: a -> b -> a
g x :: b -> a
g . g x :: b -> b -> a
Consequently the principal type is
f :: (a -> b -> a) -> a -> b -> b -> a
One can check the constraints by expanding the composition:
f g x y z = g (g x y) z
Here g x y has type a, and the second application accepts it and the final b-typed argument z. The two type variables remain independent; no Haskell type class constraints are required.
The principal type needs an equality type class constraint:
isPrefix :: Eq a => [a] -> [a] -> Bool
isPrefix [] _ = True
isPrefix (_:_) [] = False
isPrefix (x:xs) (y:ys) = x == y && isPrefix xs ys
The empty list is a prefix of every list, witnessed by the second list itself as the remaining suffix. A nonempty prefix cannot fit in the empty list. Otherwise the first elements must agree, and the remaining first list must be a prefix of the remaining second list. These cases prove correctness by structural induction on the candidate prefix. The Eq a constraint is required by x == y; no ordering constraint is needed.
Application binds more tightly than function composition, and (.) associates to the right. Let
g :: a -> b -> c
Then y :: a and g y :: b -> c. To compose to its left, h x must have type c -> a, so x :: d and h :: d -> c -> a. The outer occurrence of g receives that a, producing a function of type b -> c. Consequently g . h x . g y has type b -> b -> c, and the principal type is f :: (a -> b -> c) -> (d -> c -> a) -> d -> a -> b -> b -> c. No Haskell type class constraint is introduced.
Assign and . The application s y x forces for a fresh type ; applying f to that result forces . No further equations identify these four type variables. The principal type is therefore
k :: (u -> v -> w) -> v -> (w -> r) -> u -> r
k s x f y = f (s y x)
Every compatible typing is an instance of this one, because type inference imposed precisely the two application constraints and no class constraints.
Principal type 2026-10-25
A principal type is a most general valid type scheme: every other valid typing of the expression is obtained by substituting types for its quantified type variables, subject to any declared class constraints. For example, \s x f y -> f (s y x) has type (u -> v -> w) -> v -> (w -> r) -> u -> r; the application constraints require precisely these connections and no equality between the four type variables. A concrete typing is an instance, not evidence that the expression needs that particular type.