这是一个比较详细的图解:http://jiyinyiyong.github.io/monads-in-pictures/
基础概念:封装值,即将值塞入一个具有上下文(context)的盒子中(上下文,接收一些输入,输出一些结果)
Functor :m a -> (a -> b) -> m b 将函数映射(fmap)至封装(m)的值中
简化操作: <$>
(<$>) :: (Functor f) => (a -> b) -> f a -> f b
接收一个函数,然后对第二个Functor中的值做map
Applicative:m a -> (m (a -> b)) -> m b 将封装的函数映射至封装的值中
简化操作: <*>
(<*>) ::(Functor f) => f (a -> b) -> f a -> f b
接收一个装有函数的Functor,然后取第其函数对第二个Functor中的值做map(函数映射)
Monad:(>>=) :: m a -> (a -> m b) -> m b 将封装了值得函数映射至封装的值中
下面是对Functor、Applicative的型别测试:
(>>>>=) :: (Functor m) => m a -> (a -> b) -> m b
x >>>>= f = f <$> x
(>>>=) :: (Applicative m) => m a -> (m (a -> b)) -> m b
x >>>= f = f <*> x
测试结果:
ghci>[1..10] >>>>= (*2)
[2,4,6,8,10,12,14,16,18,20]
ghci>[1..10] >>>= [(+1),(*2)]
[2,3,4,5,6,7,8,9,10,11,2,4,6,8,10,12,14,16,18,20]
最后是Monad:正如Applicative是加强版Functor,Monad是加强版Applicative;通过上面(>>=) 的型别声明,可以知道monad是将一个接收值并返回封装值得函数应用到封装的值中,听起来很绕,其实看下面几段测试就很好理解了
ghci>[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
上面通过(>>=)连接了我们的monad(即list ([a]),注意list的Monad声明中return x = [x])
[1,2]是封装的值 ,\ch -> return (n,ch) 接收一个值并返回封装的值,这里\n -> ['a','b'] >>= \ch -> return (n,ch)转化之后为 \n -> [(n,'a') , (n,'b')]然后替换到原式中得到:
[1,2] >>= \n -> [(n,'a'),(n,'b')]
在看看下面一段:
ghci>[(n,ch)|n<-[1,2],ch<-['a','b']]
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
list的语法糖,或者另一种变种:
do
n <- [1,2]
ch <- ['a','b']
return (n,ch)
不论是list comprehension 或是用 do 表示法来表示,他都会转换成用 >>= 来做计算。
--------《LEARN YOU A HASKELL FOR GREAT GOOD》 page 269
--------------------------------------------------------------------------------------完结散花--------------------------------------------------------------------------------------