I use polymorphic variants when I want to distinguish several datatypes t1
, t2
etc., but they logically share some of their constructors, and in particular I am interested in turning a t1
into a t2
by handling just the constructors that differ, and having a simple “otherwise return the input” other case.
type common_t = [
| `A of bool
| `B of float
]
type t1 = [
| common_t
| `C1 of int
]
type t2 = [
| common_t
| `C2 of string
]
let transform : t1 -> t2 = function
(* one case for all common constructors *)
| #common_t as v -> v
(* the interesting, non-common cases *)
| `C1 n -> `C2 (string_of_int n)
Advanced language features come with their own usability costs, so I avoid them – including polymorphic variants – whenever it is easy to do so. Maybe the common cases are simple enough, or the number of different versions is low enough, that just using normal variants is enough; then I do it. Either several distinct variant types, or just one variant type that allows all constructors at once. But when the amount of software defect due to allowing everything at once becomes high, polymorphic variants are a nice solution to reason statically on the variants without too much duplication.