title | description | canonical |
---|---|---|
Extensible Variant |
Extensible Variants in ReScript |
/docs/manual/v11.0.0/extensible-variant |
Variant types are usually constrained to a fixed set of constructors. There may be very rare cases where you still want to be able to add constructors to a variant type even after its initial type declaration. For this, we offer extensible variant types.
<CodeTab labels={["ReScript", "JS Output"]}>
type t = ..
type t += Other
type t +=
| Point(float, float)
| Line(float, float, float, float)
var Caml_exceptions = require("./stdlib/caml_exceptions.js");
var Other = Caml_exceptions.create("Playground.Other");
var Point = Caml_exceptions.create("Playground.Point");
var Line = Caml_exceptions.create("Playground.Line");
The ..
in the type declaration above defines an extensible variant type t
. The +=
operator is then used to add constructors to the given type.
Note: Don't forget the leading type
keyword when using the +=
operator!
Extensible variants are open-ended, so the compiler will not be able to exhaustively pattern match all available cases. You will always need to provide a default _
case for every switch
expression.
<CodeTab labels={["ReScript", "JS Output"]}>
let print = v =>
switch v {
| Point(x, y) => Console.log2("Point", (x, y))
| Line(ax, ay, bx, by) => Console.log2("Line", (ax, ay, bx, by))
| Other
| _ => Console.log("Other")
}
function print(v) {
if (v.RE_EXN_ID === Point) {
console.log("Point", [v._1, v._2]);
} else if (v.RE_EXN_ID === Line) {
console.log("Line", [v._1, v._2, v._3, v._4]);
} else {
console.log("Other");
}
}
Fun fact: In ReScript, exceptions are actually extensible variants under the hood, so exception UserError(string)
is equivalent to type exn += UserError(string)
. It's one of the very few use-case where extensible variants make sense.
We usually recommend sticking with common variants as much as possible to reap the benefits of exhaustive pattern matching.