博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
图解 Functor , Applicative 和 Monad
阅读量:6676 次
发布时间:2019-06-25

本文共 3726 字,大约阅读时间需要 12 分钟。

  1. 原文是站在Haskell方面写的,其中涉及到一些Haskell 中的方法。这些方法对于JavaScript开发者可能不太容易理解,所以可以去看

  2. 上面的链接都需要梯子,想要看国内的文章,可以去

下面是一个值:

我们都知道怎么运用一个函数到这个值上面:

备注:图中(+3)表示的是一个进行加3操作的函数。

很简单。让我们扩展一下,任何值都可以被放入一个上下文中。所以现在你可以将上下文想象成一个盒子,你可以将一个值放进这个盒子中。

现在当你在这个值上使用函数时,根据不同的上下文,你会得到不同的结果,这就是FunctorApplicativeMonadArrow等的基础概念。如图:数据类型 Maybe定义了两个相关的上下文。

data Maybe a = Nothing | Just a复制代码

很快我们会看到对一个Just a和一个Nothing来说函数应用有什么不同. 首先我们来说Functor(函子)

Functor

当一个值被封装在一个上下文里, 你就不能拿普通函数来应用:

这时,fmap出现了,fmap 知道怎样将一个函数应用到一个带有上下文的值,举个例子,也许你想将(+3)运用到Just 2上面,使用fmap

> fmap (+3) (Just 2)Just 5复制代码

fmap让我们知道它怎么做到的,但是fmap怎么知道怎么将函数运用到这个值上?

Functor是什么?

Functor是一种,下面是它的定义:

typeclass是一类定义了一些行为的接口。如果一种数据类型是typeclass,那么这种数据类型就支持和执行在typeclass中描述的行为。

一个Functor是定义了fmap的工作原理的任何数据类型,下面就描述了fmap怎么工作的:

所以我们可以这样做:

> fmap (+3) (Just 2)Just 5复制代码

fmap神奇地运用了这个函数。因为Maybe是一个 Functor,它申明了fmap怎么运用到JustNothing

instance Functor Maybe where    fmap func (Just val) = Just (func val)    fmap func Nothing = Nothing复制代码

当我们写下fmap (+3) (Just 2)时,它正发生着下面的事情:

所以接下来你很喜欢已有的fmap,但是当你运用(+3)Nothing上呢?

> fmap (+3) NothingNothing复制代码

另一个例子,当你运用一个函数到一个list上面会发生什么呢?

Lists也是functor,下面是它的定义:

instance Functor [] where    fmap = map复制代码

最后一个例子:当你将一个函数运用到另一个函数上回发生什么呢?

fmap (+3) (+1)复制代码

下面是一个函数:

下面是一个函数应用到另一个函数上面:

结果也是另外一个函数:

> import Control.Applicative> let foo = fmap (+3) (+2)> foo 1015复制代码

所以函数也是Functor

instance Functor ((->) r) where    fmap f g = f . g复制代码

当你在一个函数上使用fmap时,其实你就是在做函数合成

注意: 目前为止我们做的是将上下文当作是一个容纳值的盒子. 特别要记住: 盒子是有效的记忆图像, 然而有时你并没有盒子. 有时你的 “盒子” 是个函数.

Applicative

Applicative把它带到了一个新的层次。使用Applicative,我们的值放在上下文中,就像Functor

而且我们的函数也可以放入上下文中:

完全理解Applicative并不是开玩笑,Control.Applicative定义了<*>, 这个函数知道怎样把封装在上下文里的函数应用到封装在上下文里的值上面:

也就是

Just (+3) <*> Just 2 == Just 5复制代码

使用<*>能带来一些有趣的情形. 比如:

> [(*2), (+3)] <*> [1, 2, 3][2, 4, 6, 4, 5, 6]复制代码

这里有一些是你能用Applicative做, 而无法用Functor做到的. 你怎么才能把需要两个参数的函数应用到两个封装的值上呢?

> (+) <$> (Just 5)Just (+5)> Just (+5) <$> (Just 4)ERROR ??? WHAT DOES THIS EVEN MEAN WHY IS THE FUNCTION WRAPPED IN A JUST复制代码

Applicatives:

> (+) <$> (Just 5)Just (+5)> Just (+5) <*> (Just 3)Just 8复制代码

ApplicativeFunctor推到了一边. “大腕儿用得起任意个参数的函数,” 他说. “用<$><*>武装之后, 我可以接受需要任意个未封装的值的函数. 然后我传进一些封装过的值, 再我就得到一个封装的值的输出!”

> (*) <$> Just 5 <*> Just 3Just 15复制代码

一个叫做liftA2的函数也做一样的事:

> liftA2 (*) (Just 5) (Just 3)Just 15复制代码

Monad

怎么学习Monad

  1. 获得计算机科学的PhD
  2. 把它扔在一边,因为在这个章节里,你不会需要它

Monad更加麻烦了

Functor应用函数到封装过的值:

Applicative运用封装过的函数到封装过的值上面:

Monad运用一个返回封装过的值的函数到一个封装过的值上,Monad有一个函数>>=(发音"bind")来做这件事。

让我们来看一个例子,熟悉的Maybe是一个monad

假设half是仅仅对偶数才可用的函数:

half x = if even x           then Just (x div 2)           else Nothing复制代码

如果我们传给它一个封装过的值会发生什么?

我们需要使用>>=将封装过的值挤进这个函数,下面是>>=的图片:

它怎么起作用的:

> Just 3 >>= halfNothing> Just 4 >>= halfJust 2> Nothing >>= halfNothing复制代码

在内部发生了什么?Monad是另外一种typeclass,下面是它的部分定义:

class Monad m where    (>>=) :: m a -> (a -> m b) -> m b复制代码

下图说明了>>=是什么?

所以Maybe是一个Monad

instance Monad Maybe where    Nothing >>= func = Nothing    Just val >>= func  = func val复制代码

下图就是MonadJust 3发生了什么:

如果你传给它一个Nothing,那就更简单了:

你也可以级联这个函数:

> Just 20 >>= half >>= half >>= halfNothing复制代码

所以现在我们知道了Maybe同时是一个FunctorApplicativeMonad。现在让我们开始另一个示例: the IO monad

3个特别的函数。getLine获取用户输入而不接收参数:

getLine :: IO String复制代码

readFile需要一个字符串(文件名)参数,返回这个文件的内容:

readFile :: FilePath -> IO String复制代码

putStrLn需要一个字符串参数,并且将它打印出来

putStrLn :: String -> IO ()复制代码

这三个函数都接收一个常规的值 (或者不接收值) 返回一个封装过的值. 我们可以用>>=把一切串联起来!

getLine >>= readFile >>= putStrLn复制代码

总结:

  1. 一个functor是一种数据类型,它需要执行Functor typecla描述的行为
  2. 一个applicative是一种数据类型,它需要执行Applicative typeclass 描述的行为
  3. 一个monad是一种数据类型,它需要执行Monad typecla描述的行为
  4. 一个Maybe遵守这个,所以它既是functor、applicative,也是monad

functorapplicativemonad之间有什么不同?

  • functor:你可以使用fmap<$>将一个函数运用到一个封装的值上
  • applicative:你可以使用<*>liftA 将一个封装过的函数运用到一个封装的值上
  • monad:你可以使用>>=liftM 将一个返回封装值的函数运用到一个封装的值上

参考资料

转载地址:http://gogxo.baihongyu.com/

你可能感兴趣的文章
URL编码与解码
查看>>
面向对象设计原则一:单一职责原则(SRP)
查看>>
Codeforces 839D Winter is here【数学:容斥原理】
查看>>
在js中怎样获得checkbox里选中的多个值?
查看>>
基于AllegroGraph实现Protege设计知识库模型的存储步骤
查看>>
线程中释放锁的方式
查看>>
VM环境下Linux虚拟机扩展存储空间操作方法总结
查看>>
PDB文件:每个开发人员都必须知道的
查看>>
深入理解生产者消费者
查看>>
EL表达式获取参数值${param.name}等
查看>>
Is there anyway to discover which ip addresses are connected to the db?
查看>>
远程桌面不能复制粘贴的解决办法
查看>>
实战案例解析电商对抗羊毛党的策略与技术
查看>>
Vivado开发工具熟悉之工具使用杂记
查看>>
spin_lock &amp; mutex_lock的差别?
查看>>
Egret Engine(白鹭引擎)介绍及windows下安装
查看>>
一个小巧的C++Log输出到文件类 (转)
查看>>
Javascript动态操作CSS总结
查看>>
ZeroMQ接口函数之 :zmq_msg_init_size - 使用一个指定的空间大小初始化ZMQ消息对象...
查看>>
Linux 配置网络
查看>>