组合子的解释(针对工作人士)

95

什么是组合子(combination)??

根据SO(程序员问答社区)的定义,它是"一个没有自由变量的函数或定义"吗?

还是按照John Hughes在他关于Arrows的著名论文中的说法:"组合子(combinator)是从程序片段中构建程序片段的函数",这样做有利于"...使用组合子编程者可以自动地构建大部分所需的程序,而不是手写每个细节。" 他接着说,mapfilter是这种组合子的两个常见例子。

一些符合第一种定义的组合子:

一些符合第二种定义的组合子:

  • map
  • filter
  • fold/reduce (推测)
  • 任意的 >>=, compose, fmap ?????

我对第一种定义不感兴趣——它们不能帮助我编写实际的程序(如果你能说服我并证明我错了,我会给你+1分)。请帮我理解第二种定义。 我认为map、filter和reduce很有用:它们使我能够以更高的层次进行编程——减少错误、代码更简洁。以下是我关于组合子的一些具体问题:

  1. 还有哪些类似于map、filter这样的组合子示例?
  2. 编程语言通常会实现哪些组合子?
  3. 组合子如何帮助我设计更好的API?
  4. 如何设计有效的组合子?
  5. 在非函数式语言(比如Java)中,与组合子相似的东西是什么,或者这些语言使用什么来代替组合子?

更新

感谢@C. A. McCann,我现在对组合子有了一个稍微更好的理解。但是还有一个问题让我困扰:

使用组合子编写的函数式程序与不使用组合子编写的函数式程序之间有什么区别?

我猜测使用组合子编写的版本会更短、更清晰、更通用,但如果可能的话,我很想听听更深入的讨论。

同时我也在寻找常见编程语言中更复杂的组合子(比如fold更为复杂的组合子)的更多示例和解释。


4
我认为map/filter/fold是高阶函数,并且我经常使用它们。但是我仍然不知道组合子是什么。 - user166390
1
@pst -- 5天前我可能会同意,但现在我不能反驳John Hughes的观点。 - Matt Fenwick
1个回答

96
编程语言实现的组合子有很多种,其中包括map、filter和reduce等高阶函数。此外,许多编程语言还提供compose、curry等常见组合子来简化代码。具体实现取决于编程语言本身以及它们所支持的函数式编程范式。那会有很大的差异。通常只有极少数完全通用的组合子——大多数是上面提到的原始组合子——因此在大多数情况下,即使这些数据结构是由其他组合子构建的,组合子也会对使用的任何数据结构有一些了解。此时通常有几个“完全通用”的组合子以及各种各样的专门形式供人们选择。在许多情况下,(适当泛化的)map、fold和unfold足以完成几乎所有需要的功能。

如何通过组合子帮助我设计更好的API?

正如你所说,通过以高级操作方式思考和它们之间的交互,而不是低级细节。

想想关于集合的“for each”风格循环的流行,它可以让您抽象出对集合进行枚举的细节。在大多数情况下,这些都是映射/折叠操作,并且通过将其作为组合子而不是内置语法,您可以通过仅应用组合子直接将两个现有循环以多种方式相互组合——嵌套一个在另一个内部,一个在另一个之后等等而不是调整大量代码。

如何设计有效的组合子?

首先,考虑在程序使用的任何数据上有意义的操作。然后考虑如何以通用的方式有意义地结合这些操作,以及如何将操作分解成较小的组件,然后再将它们连接起来。主要是使用“转换”和“操作”,而不是直接的“行动”。当您拥有一个函数只是以不透明的方式执行某些复杂的功能并仅输出某种预消化的结果时,您无法做太多事情。把最终结果留给使用组合子的代码——你需要的是带你从A点到B点的东西,而不是期望成为过程的起点或终点的东西。

组合子在非函数式语言(比如Java)中类似于什么?或者这些语言用什么来代替组合子?
呵呵呵,很有趣的问题。因为对象本身就是一种高阶的东西——它们拥有一些数据,但也携带着一堆操作,而好的面向对象设计很大程度上归结为“对象通常应该像组合子一样行事,而不是数据结构”。
因此,最好的答案可能是,它们使用具有许多getter和setter方法或公共字段的类,并且逻辑主要由执行某些不透明的预定义操作组成。

很棒的答案!让我们看看我是否理解了——组合子并不是特殊的,而是构建可维护软件系统的一个重要部分?在你的帖子中,我仍在努力消化休斯的“程序片段”声明。 - Matt Fenwick
1
@Matt Fenwick:我非常确定他只是使用“程序片段”来表示我所说的那种转换/操作,特别是如果这是在谈论“箭头”,它更强调这种方法。此外,我不会说这是关于构建可维护系统--更多地是关于以一种特定的方式构建高度模块化和解耦的系统,具有相关的好处。 - C. A. McCann
你知道哪些好的资源可以阅读组合子的实际用法吗?非常感谢! - Matt Fenwick
1
@Matt Fenwick:抱歉,我脑海中没有关于这个问题的具体解答。然而,函数式编程风格的程序通常采用自然的方法,因此花些时间学习那些强烈鼓励这种编程方式的语言(例如Haskell或Clojure),并查看该语言中如何编写干净和惯用的代码,是一个相当不错的方式来掌握这种风格。 - C. A. McCann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接