Haskellの代数データ型をJava的なインタフェースと捉える

2017-05-20 / [haskell]

Haskellの代数データ型は僕にとってJavaのインタフェースに近い。

データ型がインタフェースでそのデータを受け取る関数がインタフェースのメソッドに相当する。 データをパターンマッチで分解して値コンストラクタ別の関数定義をするのは、 インタフェースに対する実装を与えているものだと考えている。

data Maybe a mapMaybe :: (a -\> b) -\> Maybe a -\> Maybe b

Maybe aというデータ型があるとする。これがインターフェース。

mapMaybeという関数があるとする。これがメソッド。

インタフェースに実装を与えていく。

まずはデータ構造としてのインタフェース実装を与える。

data Maybe a = Just a | Nothing mapMaybe :: (a -\> b) -\> Maybe a -\> Maybe b

ここでは Just aNothing という2種類の実装が Maybe a というインタフェースに対して与えられた。

次に関数としてのインタフェース実装を与える。

data Maybe a = Just a | Nothing mapMaybe :: (a -\> b) -\> Maybe a -\> Maybe b mapMaybe f (Just x) = Just (f x) mapMaybe f Nothing = Nothing

mapMaybe の具体的な定義が各インタフェース実装ごとに記述できた。

mapMaybe のユーザから見れば、 Maybe a の実態が Just a なのか Nothing なのかは意識する必要がない。 これが抽象化されているということ。

オブジェクト指向言語ではvirtualmethodなどを定義して、実行時に実装へと ディスパッチ する。 それと同じように上記の例ではパターンマッチを使って値コンストラクタを判定し、 ディスパッチ している。

この考え方で割りと上手くいく。ライブラリでも作らない限りはこの考え方で多くのことが抽象化できる。 しかしユーザが拡張可能なインタフェースを定義しようと思うと、問題もあったりする。

Expression Problem

Maybe a の値コンストラクタを増やした、つまり Maybe a インタフェースに新しい実装を与えたときに、Maybe a にパターンマッチをかけている関数も修正しなければならくなるし、再コンパイルが必要になってしまう、という問題。

これを解くために型クラスを使ったインタフェースの定義をしていくことになる。

で、tagless finalってなんだろうというところが今。

追記

先日「型クラスはインタフェースなのか否か」みたいな議論があった。 型クラスを正しく捉えようという目線からは、「インタフェースじゃない」という意見があった。 型クラスをファーストステップとしてアナロジーで理解しようという目線からは、「インタフェースみたいなもの」という意見があった。

視点が違えば違う言い方になるよな、というだけなのでどちらでもいい。

僕は fumieval さんの記事にあるように 「性質の表現」くらいにしか考えていないので、あんまりアナロジーとして見ていない。

といいながらこの記事ではADTとインタフェースのアナロジーを語っているところに何か破綻があるような気もするけれど。