モナド
モナドはバインド (>>=)関数をサポートするアプリカティブファンクターの一種。
(>>=)関数の定義。
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
Monadという文脈に包んだ値を、値を取る関数に対して渡す。と読める。
適用例。
ghci> Just 3 >>= (\a -> Just(a * 3)) Just 9
つまり、モナドとは値を取る関数に適応できる値を持もてる、型クラスのインスタンスだと思う。
モナド型クラス
class Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b x >> y = x >>= _ -> y fail :: String -> m a fail msg = error msg
returnはIOアクションを扱った時と同じく、文脈に包んで値を返す。アプリカティブファンクターのpureと同じ役割のメソッド。
Maybe モナド型インスタンス
instance Monad Maybe where return x = Just x Nothing >>= f = Nothing Just x >>= f = f x fail _ = Nothing
関数の実行例
ghci> return "WHAT" :: Maybe String Just "WHAT" ghci> Just 9 >>= \x -> return $ x*10 Just 90 ghci> Nothing >>= \x -> return $ x*10 Nothing
最終的に文脈を返すことで、"失敗するかもしれない"や"複数の値が並んでいるかもしれない"、はたまた"副作用があったかもしれない"など、使い手は意識することができる。
do記法
複数モナドの連結は
ghci> Just 3 >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y))) Just "3!"
このような関数定義におきかえることができ、
foo :: Maybe String foo = Just 3 >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y)))
そしてdoを使って分かりやすく記述することができる。
foo :: Maybe String foo = do x <- Just 3 y <- Just "!" Just (show x ++ y)”
パターンマッチの失敗
wopwop :: Maybe Char wopwop = do (x:xs) <- Just "" return x
このようなパターンマッチの場合、パターンマッチは失敗するのだけど、do構文内での失敗時はfail関数が実行される。try ~ catchのようですね。
リストモナド
リストモナドの定義
instance Monad [] wher return x = [x] xs >>= f = concat (map f xs)
関数fは[]を返す関数になる。つまり、concatを行わないと、[[]]という文脈になり、関数の型と合致しなくなる。よってconcatでフラットにしている。という解釈。
この例がわかりやすい。バインドに渡す関数は[x, -x]と文脈つきで結果を返す必要がある。結果は[[3,-3],[4,-4],[5,-5]]とはならない。
ghci> [3,4,5] >>= \x -> [x,-x] [3,-3,4,-4,5,-5]
またリストモナドの連結は
ghci> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n, ch) [(1,'a'),(1,'b'),(2,'a'),(2,'b')]
となり、do記法で>>=関数を置き換えると、
listOfTuples :: [(Int, Char)] listOfTuples = do n <- [1,2] ch <- ['a','b'] return (n, ch)
ghci> listOfTuples [(1,'a'),(1,'b'),(2,'a'),(2,'b')]
となる。
Scalaのfor内包表記のようですね。
guard関数
モナドにはguard関数という関数が利用できる。
guard :: (MonadPlus m) => Bool -> m () guard True = return () guard False = mzero
Trueが与えられた時は空のタプルを、Falseの時はmzeroリストの場合は値のない[]を返す。
import Control.Monad ghci> [1..5] >>= (\x -> guard (True) >> return x) [1,2,3,4,5]
Trueの場合は [()] >>= _ -> return xでreturn xが返る。
ghci> [1..5] >>= (\x -> guard (False) >> return x) []
Falseの場合は[] >>= _ -> return xで[]が返る。
また、[] >>= \x -> return xとなると、失敗を投げるので、guardはdo構文内でFalseを受け取ると、手続き型言語でいうところのcontinueのような挙動をとる。
import Control.Monad listOfTuples :: [Int] listOfTuples = do n <- [1,2,3] guard (n /= 2) return n
ghci> listOfTuples [1,3]
