十年学会函数式编程:入门的最差实践

不知道大家有没有类似的体验,有些概念如果直接告诉你它是什么,无论如何都理解不了。但是如果再告诉你它不是什么,可能就恍然大悟了。同样的,有些事情告诉你怎么做(Dos)往往不如告诉你别怎么做(Don’ts)来的更有效。回顾我学习FP的历程,确实存在这样一些最差实践,如果避开这些坑,入门应该是不需要十年的。

第一点,抛开实际应用搞FP。 FP领域内有很多抽象的理论和概念,比如类型系统、范畴论、Referential transparency等等,初次接触会觉得这些东西非常高大上,不由自主地陷入到细节里面。我花了大量的时间研究这些概念,真的是非常难以理解,越看不懂就越想征服它。最终发现这些看起来很酷的东西却没什么卵用,我也并没有成为更好的程序员(除了头发更少一点之外)。

我知道Monad遵守结合律和同一律,也知道flatMap可以接受一个函数参数f: A => M[B],并且把M[A]变成M[B]。然后呢?没有然后了,我还是不知道这些知识能解决什么问题。一度认为是我了解的还不够多,所以进一步去研究范畴论、集合论、lambda演算,却发现越走越远了,最初的疑问还是没解决。

那么问题出在哪儿呢?打个不一定科学的比喻,挖隧道可以只从一头挖,也可以两边同时开工,只从一头挖进度肯定慢,感觉永远都挖不到头,没有希望,很可能中途就放弃了。研究FP的概念和理论,可以算作一头。只从这头开始挖,可能根本就挖不动。另一头就是FP的应用了。结合具体应用,才能知道这些理论可以解决哪些实际问题,才能真正的理解这些理论。而且有个额外的好处,当把这些概念加以应用之后,会发现原来非常困难的理论那头挖起来也相对容易了。所以整个的“工期”就会变为一半,甚至是三分之一、四分之一。

所以搞FP一定不能离开实际应用。向自己提出一些问题:我之前写的代码如果用FP来写是什么样子的,能带来什么好处?当下遇到的问题用FP可以解决吗?一定可以挖掘出一些应用的场景。这个原则并不仅仅局限于FP,如果因为好奇而去研究某个技术,只能停留在非常肤浅的层面。

第二点,通过Haskell来入门FP。 Haskell当然是很牛逼的,拥有各种超前的特性。以至于我后来接触了很多语言,都会发现它们所宣称的某某新特性Haskell早就有了。Haskell还是纯函数式的编程语言,强迫你只能用FP的思维方式来写代码。Haskell很强大,但是难度也很大,没有FP基础直接硬啃会非常的难受。我曾经数次尝试学习Haskell,Learn you a haskell for great good这本书写的浅显易懂,每次头脑一热就看一点,但是也只能保持几天的热度,过几天就没兴趣了。等到下次再头脑发热的时候,却发现之前看过的东西全忘了,又要从头开始。

核心的问题还是没有机会应用Haskell。

Haskell当然可以解决实际的工程问题,web framework有Yesod,数据库操作有HDBC。但是你能在公司的业务里使用Haskell吗?真的用起来老板和同事们一定会崩溃的。在学习Haskell的时候我只能写写玩具代码愉悦自己,没法用它去解决实际业务中的需求。就像有一把很强大的武器,但是只能在演习的时候自己玩一玩,没法带上战场。不用过几天就忘了。

相比之下,Scala就是非常适合入门FP的语言了。这些年Spark的火热大大促进了Scala的普及,没有Spark之前可能大家都不会写scala,但是现在几乎每个公司每个团队都要写一些Spark的应用了。Scala是多范式的语言,既能FP,也能OO(Object Oriented),门槛没有Haskell那么高,入手相对简单。关键是可以在实际业务中使用。

社区内一直流传着一句话,“Scala is a gateway drug to haskell”。熟悉scala之后再来看haskell,这样的过渡是非常自然的。如果从Haskell开始,就相当于玩游戏选择Hard模式了。Scala标准库有很多FP相关的内容,比如Option、Either、Try这些Monad,只要用这些类型就一定会接触到FP的概念,很自然的就会考虑FP的写法。

Haskell只需要了解最基础的语法即可,有不少的文章都是用haskell来表达的,掌握一点基础的语法有助于理解。Learn you a haskell for great good的前3章快速看一看就够了,如果有遇到语法不明白的再去查阅即可。

第三点,以为范畴论就是FP的全部。 网上有很多讲FP的文章,但是其中很大一部分的关注点都过于片面。分为两个极端,一个极端是非常肤浅,只会讲一些最简单的东西,比如函数是一等公民,还会告诉你用了map、reduce、filter这些高阶函数就是FP了。另一个极端是直接快进到范畴论,很容易让人产生不懂范畴论就没法搞懂FP的错觉。为什么大家都喜欢讲范畴论呢?因为有难度,可以讲的东西很多。另外一个直接的原因是,讲讲范畴论确实是可以装逼的,抛几个公式出来,文章的逼格瞬间就提升了。最著名的莫过于下面这句话了:

其实不懂范畴论一点也不影响你搞FP,就像不会微积分也不影响买菜一样。范畴论当然有价值,但它只是考卷上的最后一道附加题,而不是必做的题目。回顾数学的发展史,也是从简单的计数、加减乘除一步一步逐渐发展到高等数学的。只有熟悉基础的内容才有可能理解更高阶的知识。这么多Monad还没搞懂怎么用就直接去搞范畴论,就好比小学一年级开始直接上微积分了。别以为Monad是多么抽象的概念,把这个想法从脑子里挤出去。Scala里的Option、Either不都是Monad吗?Monad本来就是非常具体的、可以实操的东西。

本文是系列文章十年学会函数式编程之一。