十年学会函数式编程

Peter Norvig 大神多年前写过一篇 Teach Yourself Programming in Ten Years,阐述了为什么学习编程是持久战,没有捷径可以走,以及应该怎么去学习编程,每个程序员都应该看一看。里面有句话讲得特别好(不仅仅局限于编程领域):

The key is deliberative practice: not just doing it again and again, but challenging yourself with a task that is just beyond your current ability, trying it, analyzing your performance while and after doing it, and correcting any mistakes. Then repeat. And repeat again.

Peter文章的“十年”是个夸张的说法,主要是为了强调学习编程没有捷径,引导大家摆脱急功近利的心态,他学编程当然不需要十年。我的这篇文章标题差不多,就多了一个Functional,但我是真的用了十年的时间才学会的,而且顶多只能算是入门而已。所以对我而言十年并不是夸张的说法,是智商真的有限。

其实函数式编程(Functional Programming,以下简称FP)并不神秘,只是很多人把它吹得过于神化了。

当年大数据火热的时候有一种描述:“Big data is like teenage sex: everyone talks about it, nobody really knows how to do it, everyone thinks everyone else is doing it, so everyone claims they are doing it.” 函数式编程也类似,只不过用户更少一些。

在最开始学 Haskell 的时候,曾经在 Stackoverflow 上看到这样一个问题:Why should I want to learn haskell? 高赞回答在解释了一通之后,抛了一个非常玄学的结论:The problem with trying to be specific, is that trying to tell a non-Haskeller what they’ll learn from Haskell is like trying to explain the color “green” to a blind guy. 言外之意就是,我现在没法和你说的太细,说了你现在也不懂。

Haskell 本身语法其实并不复杂,很多人觉得它难是因为没接触过 FP。所以很多问题表面上是关于 Haskell 的,但其实都是关于 FP 的。类似的说法还有“学会 Haskell 会让你变成更好的程序员”。对什么都不懂的小白来说,上面的回答有利有弊。好的地方在于它可以激发你的好奇心,让你有兴趣继续研究下去。弊端在于刚开始接触就给人造成 FP 很难很高冷的印象,也可能会让人觉得 FP 是万能的,莫名地产生优越感,优越感使人盲目。

FP 究竟会让你产生什么样的变化?我试着解答一下,举两个例子说明,不确定能否解释清楚,不过至少比“说了你也不懂”负责任一些。

掌握更好的错误处理方式。 写程序不可能只考虑 happy path,错误是无法避免的。Happy path 的逻辑只有10行,如果加上完整的错误处理代码可能会膨胀到100行。这往往是导致代码难以维护的原因之一。在非FP的世界里只有两种错误处理方式:返回码和异常。通过返回码来处理错误是非常原始的做法,它会导致调用链上的每个函数都要考虑错误码,很容易写出大段的错误处理代码。

抛异常也不是完美的解决方法,是否应该用异常一直是个争议话题。学习FP之后才了解到 OptionalEither 这些为处理错误而生的类型。某个深层的函数抛异常了,外部代码可能根本不知道,也不会处理异常。但如果函数返回了 Optional,整个调用链都是知道的。当然你也可以当 Optional 不存在,直接使用 get,毕竟任何语言都不应该阻止程序员搬起石头砸自己的脚。

提高了抽象能力。 虽然面向对象强调抽象和封装,在面向对象的世界中,我只关注实现的细节。抽象和封装一个类是有本质区别的。抽象意味着小到业务逻辑,大到系统架构,都要进行更深入和更高层次的思考,数据模型是什么样的,是否有多个流程存在着共同点,怎样描述逻辑才能方便以后的扩展。对同一个业务的理解,现在和过去是完全不一样的。甚至在某些时刻会有一种 上帝视角 的感觉,仿佛知晓了世间万物的运行原理,过去在业务上遇到的很多问题都不再是问题,同时也发现了很多业务的优化空间。

我想写一写为什么我花了这么久学习函数式编程,走了哪些弯路,希望大家可以花更短的时间就掌握FP。虽然讲函数式编程的文章非常多,但却还有很多的空白区域,没有人关注,也没人讲明白,而这些空白恰恰是希望当时就有人告诉我的。我尝试把这些空白填上,而不是重复造轮子。原本想只用一篇文章就能搞定,但是FP涵盖的内容太多了,一篇实在讲不完,因此分为一系列的文章。大概包含以下内容,后续逐步更新。

  1. 入门函数式编程的“最差实践”
  2. ADT(Algebraic Data Types)
  3. Type Class
  4. FP 的一个具体的应用