胡扯flatMap in Scala

初识flatMap记得还是学Spark的时候,看到RDD有这么个接口函数,当时只觉得其功能和map有些像(把list的每个元素map成一个list,再把这些list合并),其名字也和这个功能很符合,其他就没什么概念了。之后,走马观花地学习了零零星星的其他FP相关的东西,又经常发现这个函数。再往后才慢慢知道这是个什么东西——flatMap没有我想象的那么普遍,也没有我想象的那么局限。简单讲一讲我对它的理解。

画外音:学过Monad的人都知道行内有一条规矩,那就是不要在网上发表自己的Monad教程,防止误导别人。。此文只是我一个菜鸟的粗浅理解,不是教程,但是此处还是应该有
Disclaimer:(如果你不知道什么是Monad,建议先去学习权威的教程) x 3,重要的事情说三遍。

flatMap没有想象的那么普遍:在Google或百度上搜,flatMap相关的网页大部分都是Scala或者Spark相关的内容。我却以为和map一样应该是个普遍的概念。虽然这个概念在其他FP里面也有涉及,但似乎发扬大的还是在Scala里(或者相同的概念在其他语言里叫其他名字或者用某某符号代替了)。

flatMap没有想象的那么局限:最开始以为list才有flatMap,其实不止list可以,Try,Future等等很多其他类型也可以进行flatMap操作。而支持flatMap的类型也被抽象出来。支持flatMap操作的类型被称为广义上的Monad(狭义的Monad要遵循Monad要满足Monad三定律)。

不过,其他Monad上的flatMap操作内容就没有list那么直接体现出flat的含义了。举个Future的例子:

val respFuture: Future[HttpResponse] = httpClient.get("http://baidu.com")
val statFuture: Future[HttpResponse] = respFuture.flatMap(resp => httpClient.post(s"http://stat.com?len=${resp.body.length}"))

这个例子里,我们先get到baidu.com的网页,然后把response正文的长度post到一个叫stat.com的网站上去。这里假定httpClient的get和post都是返回future。注意第二行为什么是用flatMap。因为可以考虑下,如果把它换成map方法,那么statFuture的类型会是Future[Future[HttpResponse]],这与我们意图是不符的。实际上Future#flatMap直接把接收的参数的返回值(这个参数本身是个函数,所以有返回值)返回了,相当于“脱了一层壳”,返回的是Future[HttpResponse]。

了解了Future#flatMap的意义,我们把它和List#flatMap对比一下,可以发现这两个方法的作用也不是那么相近对吧?因为毕竟Future和List这两个东西就很不一样。。那他们为啥都叫flatMap呢?然后下面我就开始胡扯了。

可以发现Future和List都有着一种同功能——作为“容器”:Future“包裹”着未来可能的完成的结果(当然实际上以callback来实现);List“包裹”着若干个元素。也就是说他们首先都要是支持泛型的类型。既然支持泛型,那么根据包裹类型的不同Future和List都能有相应的不同类型,比如Future[String]和Future[Int], List[String]和List[Int]。我们经常会有给定一个Future[String]得到一个Future[Int],或者给定一个List[String]得到一个List[Int]的需求,而map和flatMap就是专门干这事的:

把一个泛型对象转换成同一泛型的另一对象

但是map和flatMap的实现方式和使用方式都不同,下面祭出我自己的理解:

11

M[*]就是泛型,其实就是Monad啦!红色箭头表示你需要传给map方法的函数;蓝色箭头表示你需要传给flatMap方法的函数。而泛型类根据其自己的map和flatMap的实现来决定如何使用你传给他的这些函数。

这里就很有意思了,我们接触到的List#flatMap和Future#flatMap的实现都是广为大众熟知的实现。但这并不代表他们的实现的功能有什么共同点,只是他们的签名具有很强的一致性罢了。实际上我也可以把List的flatMap实现定义成:把原List每个元素map成的List各自里面的最大值拼成一个新的List作为结果。这种奇怪的定义完全符合flatMap的要求,但是没什么卵用罢了,所以他不可能是默认的内置实现。

相比而言map方法似乎就没有这样的发挥空间了,至少对于Future和List而言,直观感觉上map的实现都应该是deterministic的。

好像胡扯完了,里面包含了太多主观的臆想,肯定有不少错误。还希望好心人能够帮我指正出来,谢谢!

Read more

iPhone 8 Plus

我于2014.10从Android叛变开始使用iPhone 6 Plus。时至今日,由于iOS 11的荼毒,6p已经不能正常用于各种日常操作了。无论是拿着扫码枪的焦急的收银员,还是不知是好是坏的ofo,等待他们的永远都有十秒左右的STW。刚好双11各种网站iPhone大促,iPhone X没啥活动,8却有不少折扣。我用惯了5.5',也出于续航考量,就在京东下手了一个8p。使用几天下来,分享一下从6p切换过来的心得。 重量尺寸 6p重量是172g,8p重量是202g,涨幅17.4%; 6p厚度是7.1mm,8p厚度是7.5mm,涨幅5.6%; 刚拿在手上的第一刻是不接受的。还好多日使用后,也就习惯了。 耳机 最操蛋的就是3.5mm耳机孔没有了,虽然送了一个"3.5mm座转Lightning头"的的转接器,然而卵用并不大。首先插拔那个3.5mm转接器的力道相当紧,两边线又非常柔弱,肯定不能经常插拔。

By Han
根管治疗

根管治疗

序号 日期 地点 到达医院时间 挂号时间 看病时间 号别 主治医师 治疗细节 X光辐射伤害 费用(挂号费除外) 1 2017.10.16 南京市第一医院 八点多 八点多 九点多 普通号 张凤格 被我描述左下7号牙疼误导,误诊为没毛病,让我回家观察。(实际为左下6号牙有问题) 2 2017.10.17 南京市第一医院 9:20am 9:30am 11:00am 专家号 蔡琴 拍X片,结论“髓角过高”。草草决定进行根管治疗。蔡大夫没空,马护士帮我钻掉蛀牙部分,并清理根管。 1 290.4 3

By Han
粤ICP备2023008280号-1