Variant
A variant is a tagged choice type. Ship has two, differing only by one knob — whether the set of cases is fixed (nominal) or extensible (structural):
| Closed variant | Open variant | |||
|---|---|---|---|---|
| Set of cases | fixed, nominal | extensible, structural | ||
| Declared | `type Status = \ | Open \ | Done` | no declaration needed |
| Case syntax | PascalCase constructors (Open, Done(T)) | :tags (:ok, :error(T)) | ||
| Built from | constructor list | a row of tags | ||
| Default? | yes — boring default for domain modeling | opt in for structural composition |
// closed variant — nominal, fixed
type Status = | Open | Done
// open variant — structural, extensible, built from a row of tags
let x: [ :ok | :error(String) ] = :ok
OCaml calls the open variant a polymorphic variant; "open variant" is the canonical Ship name. The nominal one is sometimes called an ADT (algebraic data type) — prefer "closed variant" in Ship prose.
How the pieces fit
A tag (
:foo) is one case of an open variant.A row is the set of tags; wrap it in
[ … ]to get an open variant type. A row is the ingredient; the open variant is the finished type.Polymorphic open-variant parameters use the open-binder (
open 'r):type Timed(open 'r) = [ 'r | :timeout(Duration) ].The error channel is a row of tags too — see error-row.md.
A row only exists at kind
Row; a finished variant is at kindType.See open-variant.md, row.md, tag.md, kind.md, and ../01-syntax.md.