型の記述に関する議論

OcamlHaskellも、どちらも型推論を搭載している言語である。これは言語の上でとても重要な意味を持っている。だが、上記のソースを見てもらってその型推論が同じに見えるだろうか?一応、やっている内容は同じなのだが。
というのは、Haskellでは基本的に関数の型を自分で記述する。型を記述というのは、例えばCで言うところのint hogehoge(int foo, int bar)みたいなものだ。それに対してOcamlではそれをしない。いや、することが少ないと言っておく。
最初、Ocamlでコーディングしたときには、この型を自分で記述しないことがすばらしい機能だと感じた。というのは、JavaGenericsでそれが嫌だったからだ。JavaGenericsはよい機能なのだが、例えば変数の型宣言でLinkedList<Vector<Integer>>>とか書いていると、気が狂いそうになる。しかも、コンストラクタにもそれを書かなくてはいけなかったりして、タイピング量がひたすらに多い。
で、Ocamlではそれをやらなくてよいのが理想だったのだが(というより、言語実装者のほうもそれを目指している感はある)、なぜHaskellではわざわざ型を書くのか。そして、どちらの方が良いのか。これを考えるために、ちょっと以下のOcamlのソースを見てほしい。

type 'a tree = Leaf of 'a | Blanch of ('a tree) list;;

let rec fringe = function
         Leaf(x) -> x
       | Blanch(x::y) -> (List.append (fringe x) (fringe (Blanch(y))))
       | Blanch([ ]) -> [ ];;

let rec samefringe x y = (fringe x) = (fringe y);;

なんだ上のソースと一緒じゃないか、と思うかもしれない。だが、実はちょっと違うのだ。どこが違うかといえば、fringe関数の2行目、Leaf(x)の返り値がxになっている。正しいソースだと[x]になるはずなのだ。で、これは今日実際に僕がやったバグなのだ。
なんと、このソースは関数自体はエラーを出さない。ただし、関数の型はちょっと変わってしまって、本来であれば

val fringe : 'a tree -> 'a list = 
val samefringe : 'a tree -> 'a tree -> bool = 

という型になるはずが、

val fringe : 'a list tree -> 'a list = 
val samefringe : 'a list tree -> 'a list tree -> bool = 

となっている。この型が違うのは気がついたのだが、どこでこの型が間違っているのかを探すのに非常に苦労した。非常にといってもたかが知れているが、この長さのソースでそんな分からない部分が発生したというのが問題なのだ。
だが、Haskellであれば、どの部分で型が違うというエラーが出る。確かに、関数の型を自分で書くのはめんどくさい。だけどもそのかわりに、どこの型が違うのかというのをきちんと教えてくれる。上記のOcamlのソースの場合はそこまで教えてくれない。関数を適用したときに、"引数の型が違う"とぼやくだけなのだ。関数型言語は関数をどんどんと作っていくのだから、関数を定義したときにその関数のエラーは出力してほしい。なるほど、納得がいく。
Ocamlの人は型を記述しようとしない。文法上できるが、やりやすいとは思わないし、やり方は教えてもらったとはいえ「型は記述しろ」とid:wpwさんに言われた記憶はない(・・・言ってないですよね?)。Haskellの人は、ガリガリ記述する。記述するのが当たり前に見える。これは文化の違いのようなものだとは思うが、しかしながら確かに型を記述するというのはバグ発見の近道だと思う。じゃあJavaにすれば、という意見もあるかもしれないが、Haskellは必要のない型は書かなくても良い。書きたいところだけ書けばいい。つまり、ここに自由度がある。
OcamlHaskellも、自分が型を書きたいところだけ書けばよいというかたちにしてはいるが、デフォルトで書かなくて良いOcamlと基本的に書く方針のHaskell。言語の強さ的にHaskellが最近強いのは、これが一要因になってるような気もする。