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を定義すればいいことになります。