Module C1.06
Discriminated unions & records
Two type-builders — and with them a domain modelled so precisely the wrong value will not compile.
The previous module read data by its shape. This one builds the shapes. F# has two
algebraic type-builders. A record is a product type: a value has field A
and field B and field C — a Point is an
X together with a Y. A
discriminated union is a sum type: a value is case A or case B
or case C — a Shape is a Circle or a
Rectangle, never both. Compose the two and the set of values a type admits matches
the set of values the domain allows, so an illegal state cannot be constructed — the third
dive makes that its lesson. These are the types the next module's collections hold, and the ones
Option and Result are themselves built from.
Sum and product, in three dives
Two builders cover the algebra of data. Product combines fields with and; the number of values a product admits is the product of its fields' value counts. Sum combines cases with or; the number of values a sum admits is the sum of its cases' value counts. Choosing the builder that matches the domain is the whole craft — and it is what makes the bad value impossible to write. Select a dive below to read its summary and the one idea it adds.
The first dive builds records — declared by their field names, immutable, compared by their
contents, copied-and-updated to a new value. The second builds discriminated unions — a
closed set of cases, each optionally carrying data, exhaustively handled by a match.
The third is the payoff: making illegal states unrepresentable, where a union that lists only
the legal combinations turns a domain rule into a compile-time guarantee.
The three dives · select one
A product type: a value has every field at once. Declared by its field names, immutable, compared by contents, and copied-and-updated to a new record with { p with Y = 3.0 }.
Open this dive · or pick another above
And-types hold every field; or-types hold one case. Compose them and the type admits exactly the values the domain allows — no more, no fewer.
The three dives
Read them in order. Each stands on valid, runnable F#: every type declaration, every constructed value, and
every FSI line below can be typed into dotnet fsi and produces exactly what the page
shows.
Records
Product types: declared by field names, immutable, structural equality, the copy-and-update expression, and anonymous records.
- productbuild it, copy-update it, compare it
Discriminated unions
Sum types: a closed set of cases, each carrying data, handled exhaustively by match — with recursive and single-case unions.
- sumone case at a time, checked exhaustively
Making illegal states unrepresentable
The design payoff: a union that lists only the legal combinations turns a domain rule into a guarantee the compiler keeps.
- designthe bad value will not compile
References
Sources
- Microsoft — F# documentation — records, discriminated unions, and the copy-and-update expression.
- The F# Language Specification — the precise grammar for record and union type definitions and their equality.
- The F# Software Foundation — the language's home and learning resources.
Related in this course
- C1.06.1 — Records — the product type, built and copied.
- C1 — F# Language — the chapter this module sits in.
- Elixir — Algebraic data types — sums and products on the BEAM, built from maps and tagged tuples.