モナド
モナドはバインド (>>=)
関数をサポートするアプリカティブファンクターの一種。
(>>=)
関数の定義。
(>>=) :: (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]