现在把绝大部分人的面向对象知识都删掉,然后你提这么一个问题,为什么要学面向对象编程?
你会发现比这个问题更难解释。
继承,多态,封装?这是大部分人给出的答案。(对应函数组合,无副作用,引用透明之类的)
另外一部分逼格较高的人说这些都是错的,他们会说OO只和messaging相关,甚至都不需要有Class,然后把“面向对象”这个词的发明者Alan Kay说的话拿出来So what *did* Alan Kay really mean by the term "object-oriented"?。(对应Monad,lazy,type class以及无数听不懂的term)。最后后者鄙视前者,前者不服,两批人打了起来,然后你从提问者变成旁观者一脸懵逼的走掉了。最后你还是不知道什么是OO。
你看上面有多少楼都是这种根本无视题主就为了说服另一群人的……
然后你可以问,为什么不用struct?然后就有无数似是而非的答案。Object是比struct好一点,但是很有限。OO之所以最终流行起来了,其实和90年代软件公司的发展和期望相关。那时候动不动就用C++全部重写,而现在各种敏捷大行其道的时候公司都学聪明了……
除了Struct这个例子,你还可以想象你把Promise介绍给还没有Ajax时代的JavaScript程序员。他们会问你这个东西有什么用,你能举个例子嘛?Promise是个类似Monad的类型,Monad就要更抽象一些,这时候有人让你用现实世界的例子类比,这是很难的,毕竟是“用来抽象抽象的抽象”。当你要表示Thing这种抽象概念的时候,无论你举出什么例子,对方都有可能过拟合地认为Thing就是你说的那类东西。
发现monad是实现了一个不安全的又好像安全的东西,typeclass和class又差不了多少(一个是一开始就填充了无数的东西,一个是慢慢的填),都是一种语法糖(Haskell data)
虽然你这几条都不对,但是这就是函数式编程的尴尬之处。这个和“为什么不用struct”的问题是类似的。假如现在有个用C写的代码库,我也没办法举出充分理由换个语言。函数式编程的这几个元素要比对应的OO概念更强大安全通用,但是项目开始的时候其实并不能有效体现出来。
其实OO本身也一样,它被发明出来是为了解决高边际成本问题,也就是你项目做到后期没有办法再加代码的情况。不然只用C就已经足够好了,很多时候C代码还要比C++代码更优雅。很多函数式编程在这方面做得更好,比如一个维护一个大型OO项目,有时候为了不复制代码要大幅重构,函数式编程至少函数和类型之间的关系是比较松耦合的。另外复杂的OO项目,因为有method的存在,经常会有很多和业务无关的Class,导致建模层次过多过于复杂,函数式编程更依赖ADT本身,所以模型的数量一般都跟真正的业务模型数量是一致的。
当在项目后期,主流OO(即Java这种偏向于命令式风格的OO)会有很多难以理解的问题,特别是于并发,锁相关的时候。如果坚持函数式编程会让这些事情变得简单甚至不复存在。当然,遗憾的是你并不能重写代码库。
现在和90年代不同的是,工程师们有了OO,就认为他们可以解决大规模软件问题,因而不愿意付出学习成本来把工作做得更好。软件公司也对软件本身有了更多认识,认为改变编程范式的短期支出过高,而不愿意尝试。这种情况导致市面上使用函数式编程的项目少,会的人少,写出来的代码没人看得懂,总也走不上良性循环。
现在类似Swift,Scala这些主流语言已经逐渐把一些函数式概念绑定到语言比较核心的位置了,类似Linq这些比较早的概念其实也涵盖了一些函数式编程的概念。现在主流语言都只是比较有限地吸收这些概念,而很少转变范式。转变范式意味着让OO程序员丢掉安全区,因而不太容易成为主流。
个人认为最重要也最好理解的是函数组合(Composition),即compose(f, g)两个函数可以得到一个fg叠加起来的函数,这就让你可以不依靠Class编写更灵活的抽象。函数组合也比较容易在很多主流语言里面应用。很多高级概念都在静态类型的函数式语言存在,很多时候都已经属于类型理论的范畴了。
另外学函数式编程不代表非得要把所有东西都学了,静态类型的函数式编程是好,但是要付出挺多的学习成本,甚至再深入的时候发现这个领域有些地方还有些空白。我不知道为什么现在黑Lisp已经成了政治正确,(先把Lisp踢出函数式编程圈子,再否认Lisp是个语言)但我认为JavaScript和Scheme这些比较轻松的动态类型语言是个比较好的开始。(函数式不流行一定程度上也和社区里面太多的Haskell概念有关)
另外直接正面回答下这个问题: