OCaml一人勉強会 - レコード、バリアント、ついでに型変数

レコードは名前付きタプルというか、Structというか。そんな感じのものです。よくある例として二次元座標とか。

# type point = { x:int; y:int };;
type point = { x : int; y : int; }
# let pt = {x=3; y=5};;
val pt : point = {x = 3; y = 5}
# pt.x * pt.y;;
- : int = 15

フィールド名を同じにしちゃうようなオイタは型推論の邪魔なのでダメらしいです。

# type hoge = { x:int };;
type hoge = { x : int; }
# {x=3;y=5};;
Characters 0-9:
  {x=3;y=5};;
  ^^^^^^^^^
The record field label y belongs to the type point
but is here mixed with labels of type hoge

用法容量を守って正しくお使い下さい。
バリアントは何かちょっと強い子。色々できる。
例えば色とか。C言語enum的に。

# type color = Black | White | Red | Blue | Green;;
type color = Black | White | Red | Blue | Green
# Black;;
- : color = Black

タグを | で書き連ねる。型名が小文字で始めないといけないのに対し、タグ名は大文字で始めないといけない。
これだけだと大したことないですが、例えば試験の成績を整理する時に0〜100までの点数に加え未受験という情報も持たせたい場合があります。
C言語などでは何らかの整数値(-1とか)を未受験として扱う、とかまあするわけですが、バリアントを使うとそのまま表現できる。

# type score = Score of int | Unexam;;
type score = Score of int | Unexam
# let erai = Score 100;;
val erai : score = Score 100
# let dame = Score 0;;
val dame : score = Score 0
# let hikikomori = Unexam;;
val hikikomori : score = Unexam

しかも再帰的な定義が出来るので二分木とか余裕で書ける。

# type tree = Leaf | Node of (tree * tree);;
type tree = Leaf | Node of (tree * tree)

さて、あとは Leaf タグに適当な型を関連付ければ良いわけですが、わざわざ int_tree とか string_tree とか定義してられません。
ここで C++ の template みたいで、Java の Generic みたいなよくわからない型変数という物を使うことで tree を一般化することができます。

# type 'a tree = Leaf of 'a | Node of ('a tree * 'a tree);;
type 'a tree = Leaf of 'a | Node of ('a tree * 'a tree)

'a は型変数と呼ばれるもので、まあ C++ の <> だと思えばいいと思います(注:ラムダのことではない)。

# let hoge = Leaf "hoge";;
val hoge : string tree = Leaf "hoge"
# let hogehige = Node (Leaf "hoge", Leaf "hige");;
val hoge : string tree = Node (Leaf "hoge", Leaf "hige")

とまあこんな感じでOCamlの型について色々見てきたわけですが、型の表現に関しては割と自由度も高く強そうです。他の関数型言語もそうなのかなあ謎。
こうある程度体系的なプログラミングなんてのはアルゴリズムとデータ構造が中心で、さらに言えばまずはデータ構造ありきだと思っているんですが、その線でいくとOCamlの型システムは非常に良いなあと思うわけです。
一方でこの記述の長さは正直にいっていただけない。

# List.length [1;2;3;4;5];;
- : int = 5
# Array.length [|1;2;3;4;5|];;
- : int = 5

あと普通こういうことはしないんでしょうが、ちょっと勘弁してくれと。

# open List;;
# open Array;;
# length [1;2;3];;
Characters 7-14:
  length [1;2;3];;
         ^^^^^^^
This expression has type 'a list but is here used with type 'b array

Objective ならこういうのできてもいいじゃないかと思うんですができない。

# [1;2;3]#length;;
Characters 0-7:
  [1;2;3]#length;;
  ^^^^^^^
This expression has type int list
It has no method length

メソッド持ってないのかよ…という。まあそんなものもってたら色々差し支えあるのかもしれませんしないのかもしれませんが、ないものは仕方ないです。