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]