型クラス
このとき 本当は ArrowChoice*1 での実装も書く予定だったけどまだ理解が足りてなかった。
それで id:nskj77 氏が書かれた Arrow のはなし を読んでいる。
読みながら実際の定義も調べているとどうやら型クラスが理解できていない。
型クラスについて調べてみる。
『Real World Haskell』 から引用
6.2 型クラスとは何か
型クラスは実際に与えられたデータの型ごとに実装を持ち得る関数の集合によって定義するものです。型クラスはオブジェクト指向プログラミングのオブジェクトに似ているようにも思えますが、全く違うものです。
『プログラミング Haskell』 から引用
3.9 基本クラス
型は互いに関連する値の集合であったことを思い出そう。この考えたに則して言えば、クラスとは、共通のメソッドを提供する型の集合である。メソッドとは多重定義された関数のことである。
自己流に理解すると
- クラスとは何かを分類するためのもの(XXX はクラスに属しているかを問える)
- Groovy ではオブジェクトを分類する
- Haskell ではデータ型を分類する
- Groovy ではクラスと呼ばれるものが Haskell でデータ型と呼ばれる
data と newtype の違いは型クラスとは直接関係ないので
という理解にしておく。
実際に Arrow のソースを読む
-- データ型 a のための Arrow クラスを定義する場合は以下のものを実装しなければならない -- データ型 a は Category クラスでなければならない -- ちなみに Category クラスのメソッドは id と (.) class Category a => Arrow a where arr :: (b -> c) -> a b c first :: a b c -> a (b,d) (c,d) second :: a b c -> a (d,b) (d,c) -- second のデフォルトの実装 second f = arr swap >>> first f >>> arr swap where swap :: (x,y) -> (y,x) -- ~ は遅延パターンだが今回は無視してもよい swap ~(x,y) = (y,x) (***) :: a b c -> a b' c' -> a (b,b') (c,c') f *** g = first f >>> second g (&&&) :: a b c -> a b c' -> a b (c,c') f &&& g = arr (\b -> (b,b)) >>> f *** g -- (->) 型 のための Arrow クラスの実装 instance Arrow (->) where arr f = f first f = f *** id second f = id *** f (***) f g ~(x,y) = (f x, g y)
(->) がデータ型であることが不思議な感じだが調べてみるとやはりデータ型だ。
Prelude> :i (->) data (->) a b -- Defined in GHC.Prim
続きを読む
-- データ型 Kleisli を定義する -- Kleisli は runKleisli フィールドを持つ newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b } -- Arrow クラスを実装するには Category クラスも実装しておかなければいけない -- Monad m => は、ここで m は Monad クラスのメソッドが定義されているデータ型の意味 -- Kleisli の runKleisli の戻り値のデータ型が Monad クラスである場合の Category クラスの実装 instance Monad m => Category (Kleisli m) where id = Kleisli return (Kleisli f) . (Kleisli g) = Kleisli (\b -> g b >>= f) -- 同じく Arrow クラスの実装 instance Monad m => Arrow (Kleisli m) where arr f = Kleisli (return . f) first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d)) second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))
Kleisli が何を意味しているのかはまだわからないけどソースは読めるようになった。
-- Control.Arrow モジュールの関数定義 -- a は Arrow クラスのメソッドが実装されているデータ型である returnA :: Arrow a => a b b returnA = arr id (^>>) :: Arrow a => (b -> c) -> a c d -> a b d f ^>> a = arr f >>> a (>>^) :: Arrow a => a b c -> (c -> d) -> a b d a >>^ f = a >>> arr f (<<^) :: Arrow a => a c d -> (b -> c) -> a b d a <<^ f = a <<< arr f (^<<) :: Arrow a => (c -> d) -> a b c -> a b d f ^<< a = arr f <<< a
以下、定義が続くけど読み方は同じ。