到目前为止,我主要接触面向对象编程,并期待着学习一门函数式语言。我的问题是:

什么时候选择函数式编程而不是面向对象?函数式编程是更好选择的典型问题定义是什么?


你不必在这两种范式之间做出选择。您可以使用许多功能概念编写具有OO架构的软件。FP和OOP本质上是正交的。

以C#为例。你可以说它主要是OOP,但有很多FP概念和构造。如果考虑Linq,允许Linq存在的最重要的构造本质上是函数式的:lambda表达式。

另一个例子,F#。你可以说它主要是FP,但有很多OOP概念和构造可用。您可以定义类、抽象类、接口和处理继承。当它使代码更清晰或显著提高性能时,您甚至可以使用可变性。

许多现代语言是多范式的。

推荐读数

由于我的处境相同(OOP背景,学习FP),我建议您阅读一些我非常欣赏的书籍:

Jeremy Miller为日常.NET开发编写的函数式编程。这是一篇很棒的文章(虽然格式很差),展示了C#上FP的许多技术和实用的现实例子。《真实世界函数编程》,作者Tomas Petricek。这是一本伟大的书,主要涉及FP概念,试图解释它们是什么,何时应该使用。F#和C#中都有很多示例。此外,Petricek的博客也是一个很好的信息来源。


如果您处于高度并发的环境中,那么纯函数式编程是有用的。缺少可变状态使得并发几乎微不足道。请参见Erlang。在多基语言中,如果可变状态的存在必须是一个实现细节,那么您可能需要对一些事情进行功能建模,因此FP是问题领域的一个好模型。例如,请参见Python中的列表理解或D编程语言中的std.range。这些灵感来自函数式编程。


你什么时候选择函数式编程而不是面向对象编程?

当您预计到不同类型的软件发展时:

当你对事物有一组固定的操作时,面向对象的语言是很好的,随着你的代码的发展,你主要添加新的东西。这可以通过添加实现现有方法的新类来实现,而现有的类则保持独立。当你有一套固定的东西时,函数式语言是很好的,随着代码的发展,你主要在现有的东西上添加新的操作。这可以通过添加使用现有数据类型进行计算的新函数来实现,而不使用现有函数。

当进化走错了方向,你就会遇到问题:

向面向对象程序添加新操作可能需要编辑许多类定义以添加新方法。向功能程序添加新类型的内容可能需要编辑许多功能定义以添加新的案例。

这个问题已经众所周知多年了;1998年,菲尔·沃德勒将其称为“表达问题”。尽管一些研究人员认为,表达问题可以通过混合语言等语言特性来解决,但一个被广泛接受的解决方案尚未成为主流。

函数式编程是更好选择的典型问题定义是什么?

函数式语言擅长以树形形式处理符号数据。一个最受欢迎的例子是编译器,源语言和中间语言很少发生变化(大部分是相同的事情),但编译器编写者总是添加新的翻译和代码改进或优化(对事情的新操作)。编译和翻译通常是功能语言的“杀手级应用”。


面向对象编程提供:

封装,至内部状态控制突变限制与内部表示的耦合子类型,允许:兼容类型的替换(多态性)一种在类之间共享实现的简单方法(实现继承)

在Haskell甚至Scala中,函数编程可以允许通过更通用的类型类机制进行替换。不鼓励或禁止可变内部状态。也可以实现内部表示的封装。请参见Haskell与OOP的比较。

诺曼的断言“向函数式程序添加一种新的东西可能需要编辑许多函数定义来添加一个新的事例。”这取决于函数代码使用类型类的程度。如果特定抽象数据类型上的模式匹配遍布整个代码库,您确实会遇到这个问题,但这可能是一个糟糕的设计。

EDITED在讨论类型类时删除了对隐式转换的引用。在Scala中,类型类是用隐式参数编码的,而不是转换,尽管隐式转换是实现兼容类型替换的另一种方法。