A topic in the Open Knowledge Graph — a free, open map of 15,290 topics and the order to learn them in.

Hindley-Milner Type System

Research Depth 95 in the knowledge graph ☐ I know this ☆ Set as goal
6topics build on this
515prerequisites beneath it
See this on the map →
Lambda Calculus FoundationsType Inference Algorithms→→Bidirectional Type CheckingPolymorphism and Type Variables+1 more
type-inference polymorphism functional-languages

Core Idea

The Hindley-Milner (HM) type system is a polymorphic type system with implicit type inference. It assigns each expression a principal type (most general type satisfying constraints). Polymorphic functions are given rank-1 types: type variables are universally quantified at the top level. HM is used in languages like ML and Haskell; it balances expressiveness with decidable type inference.

Explainer

From your work with type inference algorithms, you know that a compiler can often determine the type of an expression without explicit annotations — by examining how values are used, it generates constraints and solves them. And from lambda calculus, you know that functions are first-class values that can be passed around, returned, and composed. The Hindley-Milner type system combines these ideas into an elegant framework where the compiler can infer the types of *all* expressions in a program — including polymorphic functions — without a single type annotation from the programmer, and it is guaranteed to find the most general type possible.

Consider the identity function `fun x -> x`. What is its type? It takes an argument and returns it unchanged, so it works on integers, strings, lists — anything. HM assigns it the principal type `∀α. α → α`, meaning "for any type α, this function takes an α and returns an α." The key word is principal: this is the most general type that is consistent with the function's definition. Any valid use of the function — applying it to an integer, a string, a pair — is an instance of this principal type obtained by substituting a concrete type for α. The guarantee that a principal type always exists and can be found algorithmically is HM's central theorem, proved independently by Hindley (1969) and Milner (1978).

The inference algorithm — Algorithm W — works by assigning fresh type variables to unknown types, generating constraints from how expressions are used (if `f` is applied to an integer, then `f`'s argument type must unify with `int`), and solving constraints through unification. Unification asks: can two type expressions be made identical by substituting type variables? `α → int` and `bool → β` unify with the substitution {α = bool, β = int}, giving `bool → int`. If unification fails (e.g., `int` vs `bool`), the program has a type error. The algorithm processes the program in a single pass, threading a substitution that accumulates all discovered type equalities.

The "rank-1" restriction is what makes this decidable. In HM, type variables are universally quantified only at the outermost level of a type scheme — you can have `∀α. α → α` but not `(∀α. α → α) → int` (where the quantifier is nested inside an argument position). This restriction, called prenex polymorphism, means that polymorphic values are only generalized at `let` bindings, not at arbitrary expression boundaries. When you write `let id = fun x -> x in (id 5, id "hello")`, the `let` binding generalizes `id` to `∀α. α → α`, and each use instantiates α independently — once as `int`, once as `string`. Without the `let`, passing the same lambda to a function expecting it to work at two different types simultaneously would require higher-rank polymorphism, which makes inference undecidable. This careful scoping of generalization is why ML and Haskell can infer all types while System F (which allows arbitrary-rank polymorphism) cannot.

Practice Questions 5 questions

Prerequisite Chain

Understanding Zero → The Number Zero → Counting to Five → Counting to 10 → Counting to 20 → Counting a Set of Objects Up to 20 → Cardinality: The Last Number Counted → Matching Numerals to Quantities → Subitizing Small Quantities → Addition Within 10 → Number Bonds to 10 → Addition Within 20 → Doubles and Near Doubles → Doubles Facts Within 10 → Near Doubles Facts Within 20 → Mental Math Strategies for Addition → Mental Math: Adding and Subtracting Tens → Addition Within 100 → Repeated Addition as Multiplication → Multiplication as Equal Groups → Multiplication: Arrays → Basic Multiplication Facts (0s, 1s, 2s, 5s, 10s) → Multiplication Facts Within 100 → Division as Equal Sharing → Division as Grouping (Measurement Division) → Division: Grouping (Repeated Subtraction) Model → Division: Fair Sharing Model → Division as Equal Sharing → Division as Grouping → Basic Division Facts → Division Facts Within 100 → Multiplication and Division Fact Families → Relationship Between Multiplication and Division → Division Facts as Inverse of Multiplication → Remainders and Quotients in Division → Division Word Problems → Multi-Step Word Problems → Solving Multi-Step Word Problems → Multiplication Word Problems → Division Word Problems → Introduction to Long Division → Factors and Multiples → Prime and Composite Numbers → Equivalent Fractions → Relating Fractions and Decimals → Decimal Place Value → Integers and the Number Line → Comparing and Ordering Integers → Absolute Value → Adding Integers → Subtracting Integers → Multiplying Integers → Introduction to Exponents → Order of Operations → Integer Order of Operations → Variable Expressions → The Distributive Property → Variables and Expressions Review → Introduction to Polynomials → Adding and Subtracting Polynomials → Multiplying Polynomials → Factorial → Permutations → Combinations → Counting Principles: Addition and Multiplication Rules → Introduction to Graph Theory → Propositional Logic Foundations → Logical Equivalences → Boolean Algebra → Boolean Type and Truth Values → Comparison Operators and Boolean Tests → Logical Operators and Boolean Algebra → Boolean Algebra and Fundamental Laws → Logic Gates Fundamentals → Implementing Boolean Functions with Gates → Karnaugh Map Simplification → Combinational Circuit Design → Flip-Flops and Latches → Finite State Machines (FSMs) → Deterministic Finite Automata (DFA) → Nondeterministic Finite Automata (NFA) → Two-Way Finite Automata → NFA to DFA Conversion (Subset Construction) → DFA Properties and Minimization Algorithms → Regular Languages: Definition and Characterization → Context-Free Grammars (CFGs) → Context-Free Grammar Properties and Ambiguity → Parse Trees, Derivations, and Ambiguity in CFGs → Context-Free Grammars in Compiler Design → Abstract Syntax Trees (ASTs) → Symbol Tables and Scope Resolution → Semantic Analysis Phase → Type Systems Overview → Unification Algorithm → Type Inference Algorithms → Hindley-Milner Type System

Longest path: 96 steps · 515 total prerequisite topics

Prerequisites (2)

Leads To (3)