Reading — step 1 of 5
Learn
~1 min readFunctors 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; `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)
- Less efficient than regular variants (tags are integer-coded, not pointer-tag)
- 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.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…