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

Polymorphism and Type Variables

Research Depth 100 in the knowledge graph I know this Set as goal
1topic build on this
523prerequisites beneath it
See this on the map →
Bidirectional Type CheckingConstraint-Based Type Checking+4 moreGenerics and Template Specialization
type-systems generics polymorphism

Core Idea

Parametric polymorphism allows functions and data structures to work with multiple types. This is more general than ad-hoc polymorphism (overloading). Implementing polymorphism requires careful handling of type variables, instantiation, and specialization.

How It's Best Learned

Implement parametric polymorphism using type variables and instantiation. Study how Java generics and C++ templates compile differently.

Common Misconceptions

Type variables are just placeholders (they are constraints on valid operations). Polymorphism requires runtime type checking (parametric polymorphism can be fully static).

Explainer

From your work with type checking, you know that a type checker walks the AST and ensures that every operation receives operands of the correct type. But what happens when you write a function like `identity(x) = x` that works for any type? Without polymorphism, you would need to write a separate version for integers, strings, booleans, and every other type — or abandon type safety entirely by using a universal "any" type. Parametric polymorphism solves this by introducing type variables — placeholders like `α` or `T` that stand for "some type, to be determined later" — allowing a single function definition to be type-safe across all types.

The key insight is that a type variable is not a concrete type but a universally quantified constraint. When you write `identity : ∀α. α → α`, you are saying: "for any type `α` you choose, this function takes an `α` and returns an `α`." The type checker enforces this contract: inside `identity`, the only thing you can do with `x` is treat it as an opaque value of type `α`. You cannot add to it, print it, or call methods on it — because those operations are not defined for all possible types. This restriction is what makes parametric polymorphism safe. If you could inspect or operate on `x` based on its runtime type, you would have ad-hoc polymorphism (overloading or type classes), which is a different mechanism with different implementation requirements.

When a polymorphic function is actually called — say `identity(42)` — the type checker performs instantiation: it substitutes the type variable `α` with the concrete type `int`, producing the specialized type `int → int` for this particular call. If you also call `identity("hello")`, a separate instantiation yields `string → string`. The Hindley-Milner system you have encountered automates this process through type inference: the compiler deduces both the polymorphic type of the definition and the instantiation at each call site, without requiring explicit type annotations from the programmer.

How this plays out in compiled code varies significantly across languages. In type erasure implementations (like Java generics and most ML compilers), the compiler verifies type safety at compile time and then generates a single version of the code that works with a uniform representation — typically pointers or boxed values. The type variables vanish entirely from the runtime code. In monomorphization implementations (like C++ templates and Rust generics), the compiler generates a separate specialized copy of the function for each concrete type it is called with — `identity_int`, `identity_string`, and so on. Monomorphization produces faster code (no boxing overhead) at the cost of larger binaries and longer compile times. Understanding this distinction matters when you move to intermediate code generation, because the IR must either represent polymorphic operations abstractly or the polymorphism must be resolved before IR generation.

Practice Questions 5 questions

Prerequisite Chain

Understanding ZeroThe Number ZeroCounting to FiveCounting to 10Counting to 20Counting a Set of Objects Up to 20Cardinality: The Last Number CountedMatching Numerals to QuantitiesSubitizing Small QuantitiesAddition Within 10Number Bonds to 10Addition Within 20Doubles and Near DoublesDoubles Facts Within 10Near Doubles Facts Within 20Mental Math Strategies for AdditionMental Math: Adding and Subtracting TensAddition Within 100Repeated Addition as MultiplicationMultiplication as Equal GroupsMultiplication: ArraysBasic Multiplication Facts (0s, 1s, 2s, 5s, 10s)Multiplication Facts Within 100Division as Equal SharingDivision as Grouping (Measurement Division)Division: Grouping (Repeated Subtraction) ModelDivision: Fair Sharing ModelDivision as Equal SharingDivision as GroupingBasic Division FactsDivision Facts Within 100Multiplication and Division Fact FamiliesRelationship Between Multiplication and DivisionDivision Facts as Inverse of MultiplicationRemainders and Quotients in DivisionDivision Word ProblemsMulti-Step Word ProblemsSolving Multi-Step Word ProblemsMultiplication Word ProblemsDivision Word ProblemsIntroduction to Long DivisionFactors and MultiplesPrime and Composite NumbersEquivalent FractionsRelating Fractions and DecimalsDecimal Place ValueIntegers and the Number LineComparing and Ordering IntegersAbsolute ValueAdding IntegersSubtracting IntegersMultiplying IntegersIntroduction to ExponentsOrder of OperationsInteger Order of OperationsVariable ExpressionsThe Distributive PropertyVariables and Expressions ReviewIntroduction to PolynomialsAdding and Subtracting PolynomialsMultiplying PolynomialsFactorialPermutationsCombinationsCounting Principles: Addition and Multiplication RulesIntroduction to Graph TheoryPropositional Logic FoundationsLogical EquivalencesBoolean AlgebraBoolean Type and Truth ValuesComparison Operators and Boolean TestsLogical Operators and Boolean AlgebraBoolean Algebra and Fundamental LawsLogic Gates FundamentalsImplementing Boolean Functions with GatesKarnaugh Map SimplificationCombinational Circuit DesignFlip-Flops and LatchesFinite State Machines (FSMs)Deterministic Finite Automata (DFA)Nondeterministic Finite Automata (NFA)Two-Way Finite AutomataNFA to DFA Conversion (Subset Construction)DFA Properties and Minimization AlgorithmsRegular Languages: Definition and CharacterizationContext-Free Grammars (CFGs)Context-Free Grammar Properties and AmbiguityParse Trees, Derivations, and Ambiguity in CFGsContext-Free Grammars in Compiler DesignAbstract Syntax Trees (ASTs)Symbol Tables and Scope ResolutionSemantic Analysis PhaseType Systems OverviewUnification AlgorithmType Inference AlgorithmsHindley-Milner Type SystemBidirectional Type CheckingConstraint-Based Type CheckingDependent Types and Value-Level Type ConstraintsSubtyping and Type BoundsPolymorphism and Type Variables

Longest path: 101 steps · 523 total prerequisite topics

Prerequisites (6)

Leads To (1)