Overview
Modern programming languages provide structured data through records and variants.
Records capture fixed labeled collections of values; variants encode distinct alternatives.
Together, they form the foundation for algebraic data types (ADTs) and safe pattern matching.
Note
Records = product types (fields combined).
Variants = sum types (one alternative active).
Pattern matching = selective decomposition of those structures.
Records: Product Structures
A record groups labeled fields together into one value.
Unlike tuples, fields are identified by names rather than position.
Example (OCaml):
type point = { x: float; y: float }
let move p dx dy = { x = p.x +. dx; y = p.y +. dy }Records make code more self-documenting and robust to field reordering.
| Concept | Tuple | Record |
|---|---|---|
| Identification | by position | by label |
| Extensibility | rigid | flexible (with optional defaults) |
| Example | (1.0, 2.0) | {x=1.0; y=2.0} |
Example
Diagram (
record_vs_tuple.svg)
Show two boxes — one positional (tuple) and one labeled (record) — both containing the same values, emphasizing how field names improve readability.
Variants: Sum Structures
A variant (also called a tagged union) represents a value that can take one of several labeled forms.
Example:
type shape =
| Circle of float
| Square of float
| Rectangle of float * floatEach constructor (Circle, Square, Rectangle) tags its data.
The compiler tracks which variant is active at runtime.
Note
Variants correspond to sum types:
Shape = Circle(radius) + Square(side) + Rectangle(width,height).
Variants can be nested, parameterized, or recursive — enabling expressive hierarchies like:
type 'a tree = Leaf | Node of 'a * 'a tree * 'a treePattern Matching
Pattern matching destructures values according to their constructors or record fields.
Example:
let area s =
match s with
| Circle r -> Float.pi *. r *. r
| Square a -> a *. a
| Rectangle (w, h) -> w *. hEach clause tests the constructor and binds its contents to names.
Pattern matching replaces chains of conditionals with a concise, declarative form.
Tip
Matching is exhaustive and typed — every case is checked for consistency by the compiler.
Nested and Guarded Patterns
Patterns can be nested or combined with guards for finer control:
match s with
| Rectangle (w, h) when w = h -> "square"
| Rectangle _ -> "rectangle"
| Circle _ -> "circle"Guards (when ...) allow conditional matching beyond constructor names.
Nested patterns can directly decompose structures:
match node with
| Node (x, Leaf, _) -> x
| Node (_, l, r) -> traverse l; traverse r
| Leaf -> ()Exhaustiveness and Redundancy
Compilers perform exhaustiveness checking to ensure that every possible variant is handled.
They also warn about redundant (unreachable) patterns.
Example:
match s with
| Circle r -> r*r
| _ -> 0 (* covers all remaining variants *)Warning
Non-exhaustive matches cause runtime errors in many languages.
Some, like Haskell and OCaml, statically detect missing cases and issue warnings.
Example
Diagram (
match_exhaustiveness.svg)
Tree visualization: branches for each variant, with missing leaves highlighted as uncovered.
Record Patterns
Pattern matching also works on record fields:
match p with
| { x; y } when x = y -> "diagonal"
| { x; y } -> "non-diagonal"Each field can be selectively matched or ignored:
| { x; _ } -> xRenaming is allowed during binding:
| { x = x_pos; y = y_pos } -> (x_pos, y_pos)Note
Partial record patterns are permitted — unmentioned fields are ignored.
Variants with Parameters
Constructors can carry arbitrary payloads:
type value =
| Int of int
| Float of float
| Pair of value * valueEach case introduces new bindings during a match:
match v with
| Int n -> string_of_int n
| Pair (a, b) -> "(" ^ show a ^ ", " ^ show b ^ ")"This recursive style forms the basis of interpreters, compilers, and type checkers.
Design Pitfalls
Warning
Missing constructors → non-exhaustive matches.
Shadowed patterns → redundant code that never executes.
Variant misuse → applying the wrong constructor or mismatched arguments.
Overly broad
_patterns → suppress exhaustiveness checking, hiding bugs.
Tip
Treat
_as a temporary placeholder during prototyping — replace it with explicit cases for maintainability.
Conceptual Summary
| Concept | Description | Example |
|---|---|---|
| Record | Labeled product of values | {x=1; y=2} |
| Variant | Tagged alternative forms | `Circle r |
| Pattern Match | Structured deconstruction | match s with ... |
| Exhaustiveness | All cases handled | compiler check |
| Guards | Conditional refinement | when w = h |
Diagram Concepts
-
record_vs_tuple.svg: labels vs positions in structured data. -
variant_tree.svg: constructors and their branches. -
match_exhaustiveness.svg: tree showing coverage analysis.