FP和OO正交吗?

时间:2010-10-16 15:59:48

标签: oop scala programming-languages functional-programming paradigms

我一次又一次地听到,我正在努力理解并验证FP和OO正交的想法。

首先,2个概念正交是什么意思?

FP尽可能地鼓励不变性和纯度,而OO似乎是为状态和变异而构建的 - 一个有点组织的命令式编程版本?我意识到对象可以是不可变的,但OO似乎意味着状态/改变我。

他们看起来像是对立的。这对他们的正交性有何影响?

像Scala这样的语言可以很容易地完成OO和FP,这是否会影响两种方法的正交性?

10 个答案:

答案 0 :(得分:68)

Orthogonality暗示两件事是无关的。它来自数学,意思是perpendicular。在通常的使用中,它可能意味着两个决定无关,或者一个主题在考虑另一个主题时无关紧要。正如这里所使用的,正交意味着一个概念既不暗示也不排除另一个概念。

object oriented programmingfunctional programming这两个概念并不相互矛盾。面向对象并不意味着可变性。许多以传统方式介绍面向对象程序的人通常首先使用C ++,Java,C#或类似语言,其中可变性很常见甚至鼓励(标准库提供了多种可变类供人们使用)。因此,很多人将面向对象的编程与命令式编程和可变性联系起来是可以理解的,因为这是他们学习它的方式。

然而,面向对象的编程涵盖了以下主题:

  • 封装
  • 多态性
  • 抽象

这些都不意味着可变性,并且它们都不包括函数式编程。所以是的,它们是正交,因为它们是不同的概念。它们不是对立的 - 你可以使用一个,或另一个,或两者(甚至两者都不)。像Scala和F#这样的语言试图将这两种范式合并为一种语言:

  

Scala是一种多范式编程语言,旨在集成面向对象编程和函数式编程的功能。

Source

  

F#是一种简洁,富有表现力且高效的功能性面向对象语言,可帮助您编写简单的代码来解决复杂问题。

Source

答案 1 :(得分:15)

  

首先,2个概念正交是什么意思?

这意味着这两个概念没有对比的想法,也没有彼此不相容。

  FP尽可能地鼓励不变性和纯度。和OO似乎是为状态和变异而构建的东西(一种有点组织的命令式编程版本?)。我确实意识到对象可以是不可变的。但OO似乎暗示了对我的状态/改变。

     

他们看起来像是对立的。它如何影响它们的正交性?

     

像Scala这样的语言可以很容易地执行OO和FP,这是否会影响2种方法的正交性?

OO是关于封装,对象组成,数据抽象,通过子类型的多态性,以及必要时的受控突变(在OO中也鼓励不变性)。 FP是关于函数组合,控制抽象和约束多态(也称为参数多态)。因此,这两个想法并不矛盾。它们都为您提供了不同类型的权力和抽象机制,这些机制当然可以用一种语言。事实上,这是构建Scala的论文!

在Google的Scala Experiment演讲中,Martin Odersky非常清楚地解释了他如何相信这两个概念--OO和FP - 彼此正交以及Scala如何优雅无缝地将这两个范例统一到一个新的范例中在Scala社区中广为人知的对象功能范例。一定要看你说话。 : - )


对象函数语言的其他示例:OCamlF#Nemerle

答案 2 :(得分:12)

  

首先,2个概念正交是什么意思?

这意味着它们不会相互影响。即函数式语言的功能性并不低,因为它也是面向对象的。

  

他们看起来像是对立的。它如何影响它们的正交性?

如果它们是对立的(即纯函数式语言不可能是面向对象的),它们根据定义将不是正交的。但是我不相信这种情况。

  

和OO似乎是为状态和变异而构建的东西(命令式编程的有点组织版本?)。我确实意识到对象可以是不可变的。但OO似乎暗示了对我的状态/改变。

虽然大多数主流OO语言都是如此,但OO语言没有理由需要具有可变状态。

如果一种语言有对象,方法,虚拟继承和ad-hoc多态,那么它就是一种面向对象的语言 - 它是否也具有可变状态。

答案 3 :(得分:7)

对于两个正交的概念意味着它们可以在任何给定的表现中独立地实现到任何程度。例如,考虑到音乐,您可以对音乐作品进行分类,了解它是如何调和的,以及它的节奏程度。 “谐波”和“节奏”这两个概念在正调和节奏片,不和谐片和心律片,以及不和谐和节奏片以及谐音和心律失常片的意义上是正交的。

应用于原始问题这意味着存在纯粹的功能性,非面向对象的编程语言,例如Haskell,纯粹面向对象的,“非功能性”语言,例如Eiffel,还有像C和C这样的语言。语言,如Scala。

简单地说,Scala是面向对象意味着您可以定义数据结构(“类”和“特征”),这些数据结构使用操纵此数据的方法封装数据,从而保证这些结构的实例(“对象”)是始终处于已定义的状态(对象的合同在其类中布局)。

另一方面,Scala是一种函数式语言意味着它支持不可变的可变状态,并且函数是第一类对象,它可以像任何其他对象一样用作局部变量,字段或其他函数的参数。除此之外,Scala中的几乎所有语句都有一个值,它鼓励您使用函数式编程风格。

Scala中面向对象编程和函数式编程的正交性意味着您作为程序员可以自由选择适合您目的的这两个概念的任意组合。您可以使用纯粹的命令式样式编写程序,仅使用可变对象而不使用函数作为对象,另一方面,您也可以在Scala中编写纯函数程序而不使用任何面向对象的功能。

Scala确实不要求您使用一种风格或另一种风格。它可以让您选择两全其美的方案来解决您的问题。

答案 4 :(得分:7)

与所有分类一样,将编程语言划分为功能,面向对象,程序等是虚构的。但我们确实需要分类,而在编程语言中,我们通过一组语言特征和使用该语言的人的哲学方法进行分类(后者受前者影响)。

因此,有时“面向对象”的语言可以成功地采用“功能”编程语言的特性和哲学,反之亦然。但肯定不是所有的编程语言特性和哲学都是兼容的。

例如,像OCaml这样的函数式语言通过词法作用域和闭包来完成封装,而面向对象的语言使用公共/私有访问修饰符。这些本身并不是不兼容的机制,但它们是多余的,而像F#这样的语言(一种主要用于与明确的面向对象的.NET库和语言堆栈协调一致的功能性语言)必须深入研究差距。

作为另一个例子,OCaml使用结构类型系统进行面向对象,而大多数面向对象的语言使用标称类型系统。这些非常不兼容,并且有趣地表示面向对象语言领域内的不兼容性。

答案 5 :(得分:6)

对象的概念可以以不可变的方式实现。一个例子是Abadi和Cardelli的书“A Theory of Objects”,旨在形式化这些想法,并且首先给对象提供不可变的语义,因为这使得面向对象程序的推理变得更加简单。

在这种情况下,传统上已就地修改对象的方法将返回一个新对象,而前一个对象仍然存在。

答案 6 :(得分:5)

您可以将函数作为对象和对象实现为函数集合,因此这两个概念之间显然存在某种关系。

  

FP尽可能地鼓励不变性和纯度

您正在谈论纯粹的函数式编程。

  

虽然OO似乎是为状态和变异而构建的

不要求对象是可变的。我会说对象和变异是正交概念。例如,OCaml编程语言为纯功能对象更新提供了语法。

  

像Scala这样的语言可以很容易地执行OO和FP

不是真的。缺少尾调用优化意味着大多数惯用的纯函数代码将在Scala中堆栈溢出,因为它会泄漏堆栈帧。例如,continuation passing style(CPS)和Bruce McAdam在论文That about wraps it up中描述的所有技术。没有简单的方法可以解决这个问题,因为JVM本身无法进行尾调用优化。

关于纯函数式编程和面向对象编程的正交性,我会说它们至少接近于正交,因为纯函数式编程只处理小型程序(例如高阶函数)而面向对象编程处理与大规模的计划结构。这就是为什么函数式编程语言通常为大规模结构提供一些其他机制的原因,例如,标准ML和OCaml的高阶模块系统,或Common Lisp的CLOS或Haskell的类型类。

答案 7 :(得分:2)

有助于我理解FP和OO之间关系的一件事是SICP书,特别是"Modularity of Functional Programs and Modularity of Objects"部分如果您正在考虑这些问题并且您有一个闲暇的周末,那么可能值得一读三章,它的美丽开放。

答案 8 :(得分:1)

我刚刚发现了OOP和FP的正交性explanation

基本思路如下。想象一下,我们正在使用数学表达式的AST。所以我们有不同类型的名词(常数,加法,乘法)和不同的动词(eval,toString)。

让我们说,表达式是(1 + 2) * 3。然后AST将是:

       multiplication
         /        \
     addition      3
      /    \
     1      2

为了实现一个动词,我们必须为每种类型的名词提供它的实现。我们可以将其表示为一个表格:

                +---------------------+-------------------------------------+
                | eval                | toString                            |
+---------------+---------------------+-------------------------------------+
| constant      | value               | value.toString                      |
+---------------+---------------------+-------------------------------------+
| addition      | lhs.eval + rhs.eval | lhs.toString + " + " + rhs.toString |
+---------------+---------------------+-------------------------------------+
| mutiplication | lhs.eval * rhs.eval | lhs.toString + " * " + rhs.toString |
+---------------+---------------------+-------------------------------------+

“正交性”来自于事实,而不是 OOP 我们将按行实施此表:我们将每个名词表示为一个类,它将具有实施每一种方法。

另一方面,在 FP 中,我们将按列实现此表 - 我们将为每个动词编写一个函数,此函数将对参数做出不同的反应不同类型(可能使用模式匹配)。

答案 9 :(得分:-1)

正交。这听起来不错。如果你接受了教育,你可以把它捆绑一下并假装。它有点像范式。

这一切都取决于你旅行的圈子以及每种类型的编程技术会给你带来什么。我已经阅读了一些关于SS的帖子,大多数来自函数式编程语言的人通常坚持认为你只能运行功能,而其他任何东西都违背思维和思维定势。

面向对象编程主要是关于捕获状态并将此状态保持为尽可能本地化,以免受任何不属于您管理状态的对象的影响。另一方面,函数式编程从不同的角度看待状态问题,并试图将状态与系统分离并将其简化为函数。是的,您可以在代码中使用这两种技术,但它们都从不同的角度看待软件的设计。

对功能编程技术有很大兴趣,主要是因为在处理多核芯片和并行编程时需要管理状态。在这个时间点,函数式编程似乎确实在处理这个方面占了上风,但是你可以使用Objects实现相同的效果。你只是想到了不同的问题。您不必挠头,试图尽可能地摆脱状态,而是在设计中查看对象,并了解如何使用设计模式,CRC和设置模式将它们配对到预期的核心位置。对象分析。然而,对象确实存在于那里,并且功能编程更加困难的地方在于分析现实世界并将其映射到可理解的计算机化系统。例如,在OO中,人物对象将是状态的封装,其具有作用于人状态的方法。在功能编程中,一个人将被分解为对人员数据起作用的数据部分和功能,附加的条件是数据应该只创建一次且不可变。

我必须承认,虽然来自OO背景,但在大多数OO语言中处理多核芯片时,我已经走了功能路线,主要是通过核心编程设计结构(如线程和委托)并传递伪数据周围的物体。这让我质疑OO编程的技术,因为它似乎没有很好地映射到这个线程设计。