
所以Monad到底是什么?
简单来说,是对某些经常出现在代码中的pattern的推广,这种推广有很多,我们一个个看:
- Semigroup
定义为
class Semigroup a where
(<>) :: a -> a -> a
其中(<>)
需要满足结合律,即
(a <> b) <> c = a <> (b <> c)
这个结构推广了所有类型封闭且满足结合律的二元运算,例如
instance Semigroup Int where
n1 <> n2 = n1 + n2
2.Monoid
定义
class (Semigroup a) => Monoid a where
mempty :: a
Monoid与对应的数学概念一样,比起Semigroup强在拥有单位元,即mempty
,它满足如下法则:
x <> mempty = x
mempty <> x = x
当然,从定义中的type constraint可看出它还需要满足Semigroup的结合律。这个结构推广了那些拥有与之结合结果不变的元素的二元运算,上面那个Semigroup就是Monoid,它的单位元是0 :: Int
。
3.Functor
定义
class Functor f where
fmap :: (a -> b) -> f a -> f b
每个Functor需要满足
fmap id = id
fmap $ f . g = fmap f . fmap g
即它将单位映射变为单位映射,并兼容函数的合成。当我们需要将一个函数“抬升”到更高的类型并且保留这个函数对原数值的效果,就可以用Functor,例如
instance Functor [] where
fmap _ [] = []
fmap f (x : xs) = f x : fmap f xs
上述代码定义了列表结构所对应的Functor,它把接收到的函数逐点应用到列表中每个元素上,最终结果还是一个列表,故而说Functor将函数抬到更高结构的同时保留原函数的效果。

4.Applicative
定义
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
可见Applicative比起Functor唯一的不同在于它允许你把函数本身“嵌入”到结构中来,看一个例子就明白了:
instance Applicative Maybe where
pure x = Just x
Nothing <*> _ = Nothing
_ <*> Nothing = Nothing
(Just f) <*> (Just x) = Just $ f x
这里,函数f
被嵌进了Maybe
中,于是可以根据其类型来产生不同效果。一个更明显的例子是列表,如下
instance Applicative [] where
pure x = x : []
fs <*> xs = [f x | f <- fs, x <- xs]

如图所示,它允许你将一列函数运用到一个列表上,并把结果拼起来。所有Applicative需满足
pure id <*> v = v -- Identity
pure (.) <*> u <*> v <*> w = u <*> (v <*> w) -- Composition
pure f <*> pure x = pure $ f x -- Homomorphism
u <*> pure v = pure ($ v) <*> u -- Interchange
5.Monad
定义
class (Applicative m) => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
Monad与Applicative类似,也允许你按照某种结构来应用函数,还是来看一些例子:
instance Monad (Either e) where
return = pure -- this should be true for any Monad
(Left l) >>= _ = Left l
(Right r) >>= f = f r
这个Monad的作用是,如果被bound(>>=
被叫做bind)的变量在Left
中,则不应用函数;若在Right
中,则正常返回结果。可见Monad很适合成序列的操作,比如:

上图中我们同时“取出”了包裹在两个列表中的元素,对它们进行操作后又放回了列表中,这种同时操作是Monad相比Applicative最大的优势之一。Monad必须满足
return x >>= f = f x -- Left Identity
x >>= return = x -- Right Identity
(x >>= y) >>= z = x >>= (\a -> y a >>= z) -- Associativity
OK, so what hell is a monoid in the category of endofunctors?
大概是这么回事,由于你可以把type看作object,函数看作morphism,(.)
和id
分别为morphism的合成运算与identity,这样会得到一个范畴Hask(虽然这实际上不是个范畴,详见 https://wiki.haskell.org/Hask 和 https://math.andrej.com/2016/08/06/hask-is-not-a-category)。于是Monad就by definition是自函子范畴上的幺半群(范畴论意义上的而非抽象代数意义上的)了。。。