wvogel日記

自分用の技術備忘録が多めです.

instance宣言(2)

まずは誤りコード(正コードは下に)

module ClassTest where
data MyList a = List a (MyList a) | Nil deriving Show

instance Monad MyList where
  return x = List x Nil
  Nil >>= f  = Nil
  List x xs >>= f = f x
  fail s = Nil

--おまけ
myConcat :: Show a => MyList a -> String
myConcat Nil = ""
myConcat (List x xs) = show x ++ myConcat xs

自分でリストモナドのinstance宣言を書いてみようと思ってやってみた。
これで一応returnは機能しますが、
instance宣言内の3行目のバインドの定義をどのようにしたら良いのか分からず戸惑っています。
本来のリストモナドの定義をみると、

m >>= f = concatMap f m

となっているんですが、じゃあ同様に、と思って

myConcatMap :: (a->MyList a)->MyList a->MyList a
myConcatMap _ Nil = Nil
myCOncatMap f (List x xs) = (f x) (myConcatMap f xs)

のようにやってみたけれど、コンパイルは通らない
なんでも、fに二つの値が適用されている、というエラーなんですが意味が分からない。
なぜでしょう??

以下のようにすると通りました。

module ClassTest where

data MyList a = List a (MyList a) | Nil deriving Show

instance Monad MyList where
  return x = List x Nil
  Nil >>= f  = Nil
  List x xs >>= f = f x
  fail s = Nil

myConcatMap :: (a->MyList a)->MyList a->MyList a
myConcatMap _ Nil = Nil
myConcatMap f (List x xs) = List (fromList$f x) (myConcatMap f xs)

fromList :: MyList a -> a
fromList (List k _) = k

--おまけ
myConcat :: Show a => MyList a -> String
myConcat Nil = ""
myConcat (List x xs) = show x ++ myConcat xs

MaybeモナドのfromJustを参考に。リストモナドの[]は非常に便利な表記ですね、そういう意味でも。


というわけで。
今回はこのコードを通してinstance宣言を学んだ訳ですが。
なんとなく雰囲気が分かりました。

instance A B where
  *----

のように記述して、
Aの型クラスのインスタンスとしてBを宣言することができ、
Aのもつその特徴(?)が、Bではどのように表現されるかを定義する。
それがinstance宣言、ということでしょうか。

今、上で書いたMyListは、型クラスMonadインスタンスですが、Monad

class  Monad m  where
    (>>=)   :: m a -> (a -> m b) -> m b
    (>>)    :: m a -> m b -> m b
    return  :: a -> m a
    fail    :: String -> m a

    m >> k  =  m >>= \_ -> k
    fail s  = error s

のように定義されています。
最低限のものを定義すれば良いので、上の場合ではバインド、return,failを定義すればいいことになります。