Haskellは型が先にある
Haskellの正規表現パッケージの使い方を見ていて、衝撃を受けました。
Prelude> :m Text.Regex.Posix Prelude Text.Regex.Posix> "abca" =~ "a" :: Bool True Prelude Text.Regex.Posix> "abca" =~ "a" :: Int 2 Prelude Text.Regex.Posix> "abca" =~ "a" :: String "a" Prelude Text.Regex.Posix> "abca" =~ "a" :: [String] ["a","a"] Prelude Text.Regex.Posix> "abca" =~ "a" :: [[String]] [["a"],["a"]]
こっ、これわっ!
=~ 関数が、結果の型を指定することで振る舞いを変えている!
Javaの場合、ソースコード上の型でオブジェクトが振る舞いを変えるようなことはありません。
しかしHaskellは型が先にあって、そこから実際に実行される関数が決定されるわけですね。Javaしか知らない私にとってこの逆転現象はかなり新鮮な驚きです。
同じようなことをJavaでやろうとすると、以下のようなコードになるでしょうか(下記コードは架空のものです)。
Regex reg = new Regex("a"); boolean match = reg.match("abca"); int matchCount = reg.matchCount("abca"); String matchText = reg.matchText("abca"); String[] allMatchTexts = reg.allMatchTexts("abca");
しかしJavaのこのようなアプローチは、決定的にHaskellと異なります。それは「Regexの振る舞いをRegexの外から追加することが出来ない」という点です。
Haskellであれば、既存の型に対して後付けで型クラスのインスタンスにすることが出来ます。これにより既存の型に対して、文脈に応じた操作を追加することが出来るようになります。
正規表現パッケージであれば RegexLike regex source => RegexContext regex source target に対していろんな型のインスタンスが定義されているし、必要とあらば自作の型に対してもこのクラスのインスタンスを作成することで、新たな動作を追加することが出来るでしょう(未検証ですが)。
「Haskellの型クラスはJavaではinterfaceに当たる」という説明をよく目にする気がしますが実はかなり異なるものですね。
Haskellの柔軟さには驚かされます。