type 宣言を使って宣言できるデータにはヴァリアントというのもある。おおざっぱに言うと「作り方に何種類か方法があるようなデータ」。Haskellでいう代数的データ型と同じと思っていいのかな。
たとえば次のようなものがヴァリアント。
# type figure = Point | Circle of int | Rectangle of int * int | Square of int ;; type figure = Point | Circle of int | Rectangle of int * int | Square of int
figure は図形を表すヴァリアントで,Point,Circle,Rectangle,Square をコンストラクタといい,ここではそれぞれ点,円,長方形,正方形を表している。コンストラクタはヴァリアントの値を作るのに使われる。
of に続くのはそれぞれの図形の大きさを表すための型(ここではすべて整数にしてある)。
ヴァリアントの値は,コンストラクタを(関数のように)適用することで作ることができる。
# let c = Circle 3;; val c : figure = Circle 3 # let r = Rectangle (3,4);; val r : figure = Rectangle (3, 4)
ただ,Rectangle のようにタプルをとるように見えるコンストラクタでも,別のところで作ったタプルに適用することはできない。
# let p = (2, 5);; val p : int * int = (2, 5) # let r2 = Rectangle p;; Characters 9-20: let r2 = Rectangle p;; ^^^^^^^^^^^ The constructor Rectangle expects 2 argument(s), but is here applied to 1 argument(s)
Point,Circle,Rectangle,Square はどれも figure 型なので,一つにリストに入れることができる。
# let figs = [Point; c; r; Square 5];; val figs : figure list = [Point; Circle 3; Rectangle (3, 4); Square 5]
もちろん関数でもひとまとめに扱える。それぞれのコンストラクタで扱いが違うときには,パターンマッチング(ヴァリアントパターン)を使う。
# let area_of_figure = function Point -> 0 | Circle r -> r * r * 3 | Rectangle (x, y) -> x * y | Square x -> x * x ;; val area_of_figure : figure -> int = <fun> # area_of_figure c;; - : int = 27 # area_of_figure r;; - : int = 12 # List.map area_of_figure figs;; - : int list = [0; 27; 12; 25]