Step 1 of 5 · Reading · ~2 min
Learn
Functors and Polymorphic Variants
Polymorphic variants start with backtick — `Tag. Unlike regular variants, they don't need to be declared in a type definition.
let x = `Red
let y = `Green
let z = `Blue
let describe = function
| `Red -> "hot"
| `Green -> "calm"
| `Blue -> "cool"
describe `Red (* "hot" *)
No type color = Red | Green | Blue needed.
Tags can carry data:
let shapes = [`Circle 5.0; `Square 3.0; `Rectangle (4, 7)]
let area = function
| `Circle r -> 3.14 *. r *. r
| `Square s -> s *. s
| `Rectangle (w, h) -> float_of_int (w * h)
The inferred type contains the polymorphic variant tags.
Open vs closed types:
[>Red |Blue]— at LEAST these tags (open — can have more)[<Red |Green]— at MOST these tags (closed)[Red |Blue]— EXACTLY these (regular)
The > and < are subtyping relations.
Combining different tag sets:
let basic_color : [> `Red | `Green | `Blue] = `Red
let shade : [> `Light | `Dark] = `Light
let combined = (basic_color, shade)
(* type: [> `Red | `Green | `Blue] * [> `Light | `Dark] *)
This flexibility is poly variants' superpower — extensible types without forcing a hierarchy.
Use cases:
- Quick prototyping (no type def needed)
- API responses with growing case sets
- Exception-like enums
Drawbacks:
- More complex type errors (subtyping confuses people)
- Slightly less efficient than regular variants: a tag is a hash of its name, so a match compares hashes rather than a small dense integer
- Pattern matching exhaustiveness checking weaker
Most OCaml code uses regular variants. Reach for polymorphic variants when the case set is genuinely open or you're prototyping.
Up nextLazy ValuesLazy Evaluation and Sequences
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…