函数式编程和面向对象编程

柏舟   新冠3年 10-19

我认为函数式编程和面向对象编程没有本质区别。

面向对象编程与其说是面向对象,不如说是面向对象和对象的关系。假如有n个对象,显然有n的平方的两倍的潜在联系。但是通过使用面向对象的管理,编写方法,可以大大控制联系的数量。换句话说,面向对象提供了一个管理联系的方法。

以smalltalk,ruby这类典型的动态的面向对象的编程语言举例。一个对象的类型是可以运行时改变的,比如存在整形、文件、数组、有序四个基类,整形和文件都支持比较方法。对于一个整形数组和文件数组,进行排序后,它们的类型就变成了整形有序数组和文件有序数组,同时它们的find方法会动态改变。这在C++里是不敢想象的,就像你直接修改对象的虚函数表一样。

这种高度动态的特性可以很方便的对状态进行管理,比如,可以使对象在特定状态存在特定方法,在特定数据结构使用优化算法。但是,在运行时修改类型信息使得程序一旦出了问题很难debug。这方面可以看看ruby的讨论。其实java也可以有类似的操作,但是反射真的很难用。所以,C++和java只能算半个面向对象语言。

虽然说函数式不可见的数据结构有利于并行编程。但事实上对象之间的联系本质上是事件产生的,那么只要并行的响应事件就可以实现无锁并行,这就是actor模型。actor模型最厉害的地方是能够很好的对状态进行建模,此外,能够容易的屏蔽本地调用和远程调用的细节。其中常见支持actor模型的语言有scala,Exilir,rust的actix。我个人使用actix编写过简单的程序,实际的感觉就像是使用数据库这种独占的资源对象,然后实体(actor)之间进行通信,但是,我水平太次,感觉actor模型使用起来很不顺手。我感觉kubenetes的pod的状态管理就有actor的影子。

函数式编程更看重计算过程,而不是具体的数据结构。只要计算过程是相同的,那么计算过程就可以复用,只需要把不同的部分抽象成符号,替换就可以了。从符号的角度来说,符号既可以是数据结构,也可以是函数,那么潜在的联系的数量是大大扩展了。最大的好处是,可以极大的提高复用能力,自由的组合计算过程,不会受到interface中方法名称的限制;但是最大的坏处是,过于自由使得联系难以管理。

总的来说,面向对象和函数式是一体两面的,本质上是对联系不同的管理方式,面向对象是对一类对象的联系进行管理;函数式是将数据和函数看成一体,自由组合。面向对象也可以伪装成函数式,比如仿函数。同样的,函数式也可以使用hash表手动实现一个虚函数表。我认为面向对象适合对象复杂,关系复杂的贴近业务的应用,而函数式更适合大数据,人工智能这种数据高度结构化的,计算过程类似的应用。