Functor
定义
定义如下:
class Functor f where
fmap :: (a ->b) -> f a -> f b
其中,fmap
需要遵守以下两个约束:
-- Identity
fmap id == id
-- Composition
fmap (f . g) == fmap f . fmap g
注:<$>
就是 fmap
相关的例子
Maybe 是如何实现 Functor 的:
instance Functor Maybe where
fmap f fa = case fa of
Nothing -> Nothing
Just a -> Just (f a)
列表是如何实现 Functor 的:
instance Functor [] where
fmap _ [] = []
fmap f x:xs = (f x): (fmap f xs)
范畴论相关
Applicative
定义
定义如下:首先需要是 Functor
的实例,然后才能是 Applicative
的实例
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
其中,<*>
和 liftA2
只需要满足一个即可,两者之间存在如下关系:
(<*>) = liftA2 id
liftA2 f x y = f <$> x <*> y
需要满足以下的约束条件:
-- Identity
pure id <*> v = v
-- Composition
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
-- Homomorphism
pure f <*> pure x = pure (f x)
-- Interchange
u <*> pure y = pure ($ y) <*> u
Functor 和 Applicative 的关系:
fmap f x = pure f <*> x
例子
Maybe 如何实现:
instance Applicative Maybe where pure = Just (<*>) Nothing = id (<*>) (Just f) = fmap f
Monad
monad 是范畴论里面的一个概念;在 Haskell 中,应该将其理解为「an abstract datatype of actions」
定义:首先要是 Applicative
的实例,然后才能是 Monad
的实例:
class Applicative m => Monad m where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b
其中:
return
和pure
是等价的,历史原因>>=
表示绑定。其第一个参数m a
被叫做「mobit」,它表示一个计算过程,这种计算产生类型a
的结果,这种计算会有一些「副作用」。第二个参数a -> m b
可以理解为:基于第一次计算的结果,选择如何进行第二次计算。
>>
运算符的实现:先进行一次计算,然后忽略计算结果,紧接着进行第二次计算,保留第二次计算的结果
(>>) :: m a -> m b -> m bm1 >> m2 = m1 >>= \_ -> m2
满足以下的约束:
-- Left Identityreturn a >>= k = k a-- Right Identitym >>= return = mAssociativitym >>= (\x -> k x >>= h) = (m >>= k) >>= h
例子
Maybe 实现 Monad 的例子:
instance Monad Maybe where return = Just Nothing >>= _ = Nothing Just x >>= k = k x
直观的理解 **liftA2**
这个函数:
关于 liftA2
这个函数,要站在 Functor
和 Applicative
的角度思考,不要关心实现细节。
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
把 f a
中的 a
提取出来,并应用在 (a -> b -> c)
这种函数中,最后再放入 f
中得到 f c
这就是「提取 lift」的含义。
这种用法的魅力
对于这样一个新类型:
data Foo = Bar Int Int Char
如果要从 String
中解析出它,只需要:
parseFoo :: Parser FooparseFoo = Bar <$> ParseInt <*> ParseInt <*> ParseChar