Haskellのお勉強 その20
class宣言
Haskellで新しい型クラスを宣言するには、class宣言を用いる。例えばEqクラスの宣言であれば、
class Eq a where (==) , (/==) :: a -> a -> Bool {- クラスメソッドの型宣言 -} x == y = not (x /= y) {- (==)クラスメソッドのデフォルト実装 -} x /= y = not (x == y) {- (/=)クラスメソッドのデフォルト実装-}
という感じ。
最初の「Eq a」は、宣言する型クラスの名前(Eq)と、型クラスの宣言に使う型変数(a)を宣言している。
このように書くことで、このclass宣言の中に現れる方変数 a は
すべてEqクラスのインスタンスという制約を課すことができる。
また、class宣言をする際、型クラスの名称は、1文字目がアルファベット大文字である必要があり、
型クラス名は型コンストラクタと同じ名前は使えないという制限がある。
2行目はクラスメソッド(==)と(/=)に型を宣言している。class宣言では型宣言を省略することはできない。
3行目以降は、それぞれのクラスメソッドのデフォルト実装。
インスタンス独自に実装しなかった場合、class宣言で指定したデフォルト実装が使われる。
この「Eq」の例では、クラスメソッド(==)の定義の中で(/=)が使われていたり、
反対にクラスメソッド(/=)の定義の中で(==)が使われているので、インスタンスでは必ず
(==)または(/=)のいずれかのクラスメソッドを定義しておかないと無限ループとなってしまうという点に着目。
継承を含むclass宣言
型クラスを継承する例としてOrdクラスの宣言を見てみる。class (Eq a) => Ord a where compare :: a -> a -> Ordering (<) :: a -> a -> Bool (>=) :: a -> a -> Bool (>) :: a -> a -> Bool (<=) :: a -> a -> Bool max :: a -> a -> a min :: a -> a -> a instance (Ord a) => Ord (Maybe a) instance (Ord a, Ord b) => (Either a b) instance Ord Integer instance Ord Float instance Ord Double instance (Ord a) => Ord [a] instance Ord () instance Ord Char instance Ord Bool instance Ord Ordering instance Ord Int {-ちなみに、この定義はGHCiインタプリタで「:info Ord」を実行すると参照できます-}
最初の「(Eq a) =>」で継承元のクラスを指定している。
このクラス宣言の中では、型変数aは「Ordクラスのインスタンス」であると同時に
「Eqクラスのインスタンス」でもある、という制約が課される。
Eqクラスの他にBoundedクラスもスーパークラスに指定したい様な場合は、
「(Eq a, Bounded a) =>」のように書けばよい。
compareからminまでをスーパークラスからそのまま使い、
残りをinstance宣言を使って、再実装しているってことですね。
instance宣言
ある型がある型クラスのインスタンスであることを宣言するには、instance宣言を使う。data Person = P String Int instance Eq Person where {- 「nameとageが一緒であれば等しい。」という実装。実際の人間の場合、あきらかに条件不足である -} (P name age) == (P name' age') = (name == name') && (age == age')
のように書くと、Person型がEqクラスのインスタンスであると宣言したことになる。
また、where節でPerson型のための(==)クラスメソッドを実装している。
deriving宣言
クラスメソッドの実装が単純に済むような場合、deriving宣言を使うとinstance宣言を自動生成してくれるらしい。data Person = P String Int deriving Eq
と宣言することで、「Person型はEqクラスのインスタンス」という宣言であると同時に、
「Eqクラスのクラスメソッドを自動生成せよ」という指示を同時に行ったこととなる。
deriving宣言は結構便利そうだが、いつでも使えるわけじゃないみたい。