Monads
Functor —-> Applicative —-> Monad
直接上公式:
1 | (>>=) :: (Monad m) => m a -> (a -> m b) -> m b |
这个函数叫bind。
它接受一个Monad 和 一个将参数包成Monad的函数,返回Monad,其中的值经过变换。
举个Maybe的例子
Maybe as Monads
1 | applyMaybe :: Maybe a -> (a -> Maybe b) -> Maybe b |
Monads 类型类
1 | class Monad m where |
理论上Monad应该继承Applicative类型对吧,然后并没有。因为Monad被想出来的时候Applicative还没出现。
return 和 Applicative里的pure差不多,都是把类型包一下
其他精华就在那个bind函数
再看看Maybe的Monad实际定义:
1 | instance Monad Maybe where |
do
do 代码块下不仅可以把IO Action操作的数拿出来耍,它本质上是对Monad chain的语法简化:
1 | Just 3 >>= (\x -> Just "!" >>= (\y -> Just $ show x ++ y)) |
do 代码块看上去像命令式的代码,其实是Monad函数链!
1 | dead :: Maybe String |
上面的dead最后等于Nothing。单行一个Monad值,相当于在链中加入>> theMonad
所以等价于:
1 | > Just "begin" >>= \x -> Just (x ++ " first") >> Nothing >>= \x -> Just $ x ++ " second" |
异常处理
monad 链里模板匹配抛异常了,monad就把 fail msg 的结果作为这一环的结果,继续下去。可以看到Maybe就直接返回Nothing,那么这个链最后的值只能是Nothing了。
List as Monad
1 | instance Monad [] where |
可以看到 >>=它 concat了一下,因为 >>=的第二个参数 a -> m b会把数组里一个元素又包成一个List,所以直接concat每个元素产生的list,感觉这就是flatMap的原型。
记不记得List comprehension里面也出现过<-这个符号,其实通过Monad和do就通了:
1 | [(n, ch) | n <- [1, 2], ch <- ['a', 'b']] |
List comprehension里还有一个filter:
1 | > [x | x <- [1..50], '7' `elem` show x] |
它用Monad的函数要怎么表示呢,原有的就不够了,需要混合Monad和Moniod。新的类型类叫MonadPlus:
1 | class Monad m => MonadPlus m where |
然后我们定义一个guard函数:
1 | guard :: (MonadPlus m) => bool -> m () |
然后就通了:
1 | > [1..50] >>= (\x -> gurad ('7' `elem` show x) >> return x) |
gurad True的时候返回[()],空Tuple可以代表任一类型,所以遇到 >>时直接返回函数后的值,即[x]被保留下来;guard False 时返回[],类似fail msg的值,[] 在List concat时被忽略。
Monad 定律
- left identify:
return x >>= f等价于return $ f x - right identify:
m x >>= retrun等于m x - Associativity:
(m >>= f) >>= g等价于m >>= \x -> f x >>= g