Haskell 的 Monad

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)

范畴论相关

范畴论中的 Functor

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

其中:

  • returnpure 是等价的,历史原因
  • >>= 表示绑定。其第一个参数 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 这个函数,要站在 FunctorApplicative 的角度思考,不要关心实现细节。

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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值