[zz]Functor, Applicative, 以及 Monad 的图片阐释

这是个简单的值:

我们都知道怎么加一个函数应用到这个值上边:

很简单了. 我们来扩展一下, 让任意的值是在一个上下文当中. 现在的情况你可以想象一个可以把值放进去的盒子:

现在你把一个函数应用到这个值的时候,根据其上下文你会得到不同的结果. 这就是 Functor, Applicative, Monad, Arrow 之类概念的基础.Maybe数据类型定义了两种相关的上下文:

很快我们会看到对一个Justa和一个Nothing来说函数应用有何不同. 首先我们来说 Functor!

Functor

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

就在这里fmap出现了.fmapis from the street,fmapis hip to contexts.fmap知道怎样将一个函数应用到一个带有上下文的值. 你可以对任何一个类型为Functor的类型使用fmap.

比如说, 想一下你想把(+3)应用到Just2. 用fmap:

>fmap(+3)(Just2)Just5

这是在幕后所发生的:

Bam!fmap告诉了我们那是怎么做到的!

So then you’re like, 好吧fmap, 请应用(+3)到一个Nothing?

>fmap(+3)NothingNothing

就像 Matrix 里的 Morpheus,fmap就是知道要做什么; 你从Nothing开始, 那么你再由Nothing结束!fmap是禅. So now you’re all like,准确说究竟什么是 Functor?嗯, Functor 就是任何能用fmap操作的数据类型. 因此Maybe是个 functor. 而且我们很快会看到, list 也是 functor.

这样上下文存在就有意义了. 比如, 这是在没有Maybe的语言里你操作一个数据库记录的方法:

post=Post.find_by_id(1)ifpostreturnpost.titleelsereturnnilend

但用 Haskell:

fmap(getPostTitle)(findPost1)

如果findPost返回一条 post, 我们就通过getPostTitle得到了 title. 如果返回的是Nothing, 我们加e得到Nothing! 非常简洁, huh?<$>是fmap的中缀表达式版本, 所以你经常是会看到:

getPostTitle<$>(findPost1)

另一个例子: 当你把函数应用到 list 时发生了什么?

List 仅仅是另一种让fmap以不同方式应用函数的上下文!

Okay, okay, 最后一个例子: 你把一个函数应用到另一个函数时会发生什么?

fmap(+3)(+1)

这是个函数:

这是一个函数应用到另一个函数上:

结果就是又一个函数!

>importControl.Applicative>let foo=(+3)<$>(+2)>foo1015

这就是函数复合! 就是说,f<$>g==f.g!

注意:目前为止我们做的是将上下文当作是一个容纳值的盒子. But sometimes the box analogy wears a little thin. 特别要记住: 盒子是有效的记忆图像, 然呵又是你并没有盒子. 有时你的 “盒子” 是个函数.

Applicative

Applicative 把这带到了一个新的层次. 借助 applicative, 我们的 values 就被封装在了上下文里, 就像 Functor:

而我们的函数也被封装在了上下文里!

Yeah. Let that sink in. Applicative 并不是开玩笑.Control.Applicative定义了<*>, 这个函数知道怎样把封装在上下文里的函数应用到封装在上下文里的值:

也就是:

Just(+3)<*>Just2==Just5

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

>[(*2),(+3)]<*>[1,2,3][2,4,6,4,5,6]

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

>(+)<$>(Just5)Just(+5)>Just(+5)<$>(Just4)ERROR???WHAT DOES THIS EVEN MEAN WHY IS THE FUNCTION WRAPPED IN A JUST

Applicative:

>(+)<$>(Just5)Just(+5)>Just(+5)<*>(Just3)Just8

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

>(*)<$>Just5<*>Just3Just15

一 applicative 看着一 functor 应用一函数

还有啦! 有一个叫做liftA2的函数也做一样的事:

>liftA2(*)(Just5)(Just3)Just15

Monad

如何学习 Monad:

拿个计算机科学的 PhD.

把她抛在一边, 因为这个章节里你用不到她!

Monads add a new twist.

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

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

Monads 应用会返回封装过的值的函数到封装过的值. Monad 有个>>=(念做 “bind”) 来做这个.

一起看个例子. Good ol’Maybeis a monad:

Just a monad hanging out

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

half x=ifeven xthenJust(x`div`2)elseNothing

我们给它传入一个封装过的值会怎样?

我们要用到>>=, 用来强推我们封装过的值到函数里去. 这是>>=的照片:

它怎么起作用的:

>Just3>>=halfNothing>Just4>>=halfJust2>Nothing>>=halfNothing

其中发生了什么?

如果你传进一个Nothing就更简单了:

酷! 我们来看另一个例子: 那个IOmonad:

明确的三个函数.getLine获取用户输入而不接收参数:

getLine::IOString

readFile接收一个字符串 (文件名) 再返回文件的内容:

readFile::FilePath->IOString

putStrLn接收一个字符串打印:

putStrLn::String->IO()

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

getLine>>=readFile>>=putStrLn

Aw yeah! 我们不需要在取消封装和重新封装 IO monad 的值上浪费时间.>>=为我们做了那些工作!

Haskell 还为 monad 提供了语法糖, 叫做do表达式:

foo=dofilename<-getLine

contents<-readFile filename

putStrLn contents

结论

functor:通过fmap或者<$>应用是函数到封装过的值

applicative:通过<*>或者liftA应用封装过的函数到封装过的值

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358

推荐阅读更多精彩内容