ようこそ。睡眠不足なプログラマのチラ裏です。

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宣言は結構便利そうだが、いつでも使えるわけじゃないみたい。