Haskell入門 第3章から抜粋
型クラスとは(まとめ)
- 型クラスは手続き型言語(クラスベース言語)でいうところのインターフェースとトレイトの特徴を併せ持った機構である
- 型クラスの役割(責務)はメソッドの定義である
- 型クラスではメソッドの定義の他、メソッドのデフォルトの挙動を実装することができる
- 型クラスのインスタンスとなった型は型クラスのメソッドの実装をすることができる
- 型クラスで実装したメソッドはインスタンス側で再実装することができる
- インスタンスになるためには型クラスのカインドを型のカインドが同形でなければならない
-
derivingキーワードを使えば型定義時にインスタンス化することができる
型クラスとは(詳細)
型クラス
定義:class 型クラス名 型変数名 where {関数1 :: 型1 関数2 :: 型2}
インスタンス
定義:instance 型クラス名 型名 where {関数 = 実装...}
型クラス(type class)は型ごとに異なる実装を適用するための機構を提供している。
型クラスは複数の型を持つことができる。
型クラスに所属する型を、型クラスのインスタンスと呼ぶ。
手続き型言語のインターフェースのようにメソッドを定義する。インスタンスはメソッドを実装して利用することができる。
トレイトのように役割に応じて柔軟に型に注入することができる。
例えばNummクラスでは(+), (-), (*)などのメソッドが定義されていてインスタンスにはWord, Integer, Int, Float, Doubleが属している。
Prelude> -- 型クラスNum情報を表示
Prelude> :info Num
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
{-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-}
-- Defined in ‘GHC.Num’
instance Num Word -- Defined in ‘GHC.Num’
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Int -- Defined in ‘GHC.Num’
instance Num Float -- Defined in ‘GHC.Float’
instance Num Double -- Defined in ‘GHC.Float’
Prelude> -- 型クラスMonad情報を表示
Prelude> :info Monad
class Applicative m => Monad (m :: * -> *) where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
{-# MINIMAL (>>=) #-}
-- Defined in ‘GHC.Base’
instance Monad (Either e) -- Defined in ‘Data.Either’
instance Monad [] -- Defined in ‘GHC.Base’
instance Monad Maybe -- Defined in ‘GHC.Base’
instance Monad IO -- Defined in ‘GHC.Base’
instance Monad ((->) r) -- Defined in ‘GHC.Base’
instance Monoid a => Monad ((,) a) -- Defined in ‘GHC.Base’
Prelude> -- 型クラスGreetingを定義
Prelude> :{
Prelude| class Greeting a where
Prelude| -- メソッドを定義
Prelude| name :: a -> String
Prelude| hello :: a -> String
Prelude| bye :: a -> String
Prelude| :}
Prelude> :{
Prelude| -- 型定義
Prelude| data Human = Human String
Prelude| data Dog = Dog String
Prelude|
Prelude| -- インスタンス定義
Prelude| instance Greeting Human where
Prelude| -- メソッドを実装
Prelude| name (Human n) = n
Prelude| hello h = "Hello, I`m " ++ name h ++ "."
Prelude| bye _ = "See, you."
Prelude|
Prelude| instance Greeting Dog where
Prelude| -- メソッドを実装
Prelude| name _ = "bow, wow!"
Prelude| hello _ = "wan, wan!"
Prelude| bye _ = "waon!"
Prelude| :}
Prelude> -- Greetingクラスを実装したHumanインスタンスを利用
Prelude> sato = Human "Sato"
Prelude> hello sato
"Hello, I`m Sato."
Prelude> bye sato
"See, you."
Prelude> -- Greetingクラスを実装したDogインスタンスを利用
Prelude> gonta = Dog "Gonta"
Prelude> hello gonta
"wan, wan!"
Prelude> bye gonta
"waon!"
型クラスのカインド
型クラスをみるとConstraintを返していることがわかる。
また、型クラスがインスタンスとして受け入れれる型を確認することができる。
たとえば、Greetingクラスはカインドが*の型をインスタンス化することができるし、Monadはカインドが (* -> *)の型をインスタンスに持つことができる。
-- Greetingクラスのカインド
Prelude> :k Greeting
Greeting :: * -> Constraint
-- Monadクラスのカインド
Prelude> :k Monad
Monad :: (* -> *) -> Constraint
-- Numクラスのカインド
Prelude> :k Num
Num :: * -> Constraint
-- Showクラスのカインド
Prelude> :k Show
Show :: * -> Constraint
型制約(型セーフな実装)
型や関数の定義などで型クラス => a -> a -> bというのを見かけるが、これは引数aは型クラスのインスタンス引数でなければならないという制約のこと。
こうしておけば引数aは型クラスで定義したメソッドを実装していることが保証される。
class 宣言における型制約
定義:class 親クラス データ型 => 型クラス データ型 where ...
親の型クラスを継承した子の型クラスを定義することができる。
子の型クラスのインスタンスは親のインスタンスでなければならない。
つまり、子の型クラスのインスタンスは親子の型クラスのメソッドをすべて実装していることが保証されている。
さまざまな型クラス
| 型クラス | 用途 |
|---|---|
| Show | show::Showは引数を文字列にする |
| Read | read::Read は文字列をデータ型に変換する※read "123"::Int |
| Eq | (==)::Eq, (/=)::Eqは2引数を比較しBoolを返す |
| Ord | (<)::Ord※(<=, >, >=)は2引数を比較しBoolを返す |
| Enum | Enumのindexと値の変換などの操作を行うメソッドが定義されている |
derivingによるデータ型定義とインスタンス化
定義:data 型コンストラクタ = データコンストラクタ deriving (クラス名)
データ型の定義時にこのように定義することによってderivingで指定したクラスのインスタンスにすることができる。