ヴァリアント

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]