FP和OO是否正交?


75

我一次又一次听到,我正在尝试理解和验证FP和OO是正交的。

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

FP尽可能地鼓励不变性和纯净性,而OO似乎是为状态和突变而构建的–命令命令编程的组织性略微?我意识到对象可以是不可变的,但是OO似乎暗示着我的状态/变化。

他们似乎是相反的。这如何影响它们的正交性?

像Scala这样的语言可以轻松实现OO和FP,这是否会影响这两种方法的正交性?

Answers:


72

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

面向对象编程函数编程这两个概念并不互不兼容。面向对象并不意味着可变性。许多以传统方式介绍给面向对象程序的人通常会首先使用C ++,Java,C#或类似的语言,在这些语言中,可变性是常见的,甚至受到鼓励(标准库提供了多种可变类供人们使用)。因此,可以理解的是,许多人将面向对象的编程与命令式编程和可变性相关联,因为这是他们学会的方法。

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

  • 封装形式
  • 多态性
  • 抽象化

这些都不意味着可变性,也不排除功能编程。是的,它们是正交的,因为它们是不同的概念。它们不是对立的-您可以使用其中一种,也可以使用两者(甚至都不用)。诸如Scala和F#之类的语言试图将这两种范例组合成一种语言:

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

资源

F#是.NET的简洁,表达性强,高效的功能面向对象的语言,可帮助您编写简单的代码来解决复杂的问题。

资源


我要质疑“ OO是关于...”的表述,因为我不认为定义的OO(函数式编程也非常涉及多态和抽象)。但是,在我没有时间之前,您将其编辑为我无法理解的内容。
Pascal Cuoq 2010年

22
Scala的发明者Martin Odersky称Scala为“后功能”语言,代表一种务实的方法,它将最有用的现有功能结合在一起,无论它们来自何处。按照这种模式,可以将Java称为功能
失调的

15
但是显然,发明人和发行人出于市场原因使用这些术语。这些引语应进行讨论,不能用作支持的论据。
Debilski

4
我不同意面向对象和功能是“完全不相关的概念”,对这两个概念都有很好理解的语言设计人员可以将这两个概念相互集成。这个答案可能是有趣的,如果你想的办法不同语言的设计师花了比较:stackoverflow.com/questions/3644251/...
SOC

尽管具有可变性,但是面向对象的编程确实已经与抽象,封装等相关联。但是,谈论“对象”的语义似乎有点费劲,而没有提及可以承受变化(即可变性)的身份。请参阅softwarefordays.com/post/…mitpress.mit.edu/sites/default/files/sicp/full-text/book/…中的
jbmilgrom

15

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

这意味着这两个概念没有相反的思想或彼此不兼容。

FP尽可能地鼓励不变性和纯度。OO似乎是为状态和突变而构建的(命令式编程的稍微组织化的版本?)。而且我确实意识到对象可以是不可变的。但是OO似乎意味着对我声明/更改。

他们似乎是相反的。它如何影响它们的正交性?

像Scala这样的语言可以轻松实现OO和FP两者,这是否会影响这两种方法的正交性?

OO涉及封装,对象组成,数据抽象,通过子类型进行的多态性以及必要时的受控突变(OO中也鼓励不变性)。FP与功能组合,控件抽象和约束多态性(又称参数多态性)有关。因此,这两个想法并不矛盾。它们都为您提供了不同种类的功能和抽象机制,这在一种语言中肯定是可能的。实际上,这就是Scala构建的基础!

在Google的Scala实验演讲中,马丁·奥德斯基(Martin Odersky)很好地解释了他如何相信OO和FP这两个概念彼此正交,以及Scala如何将这两种范式优雅地,无缝地结合成一个新的范式,在Scala社区中广为人知。对象功能范式。必须看着你说话。:-)


对象功能语言的其他示例:OCamlF#Nemerle


1
这是可以轻松下载的YouTube链接〜> youtube.com/watch?v=01rXrI6xelE
letronje 2010年

2
我的观点是,尽管oop和fp通常并不相反,但在某些方面您必须选择函数或oop aprox。oop的特征之一是绑定数据和行为,在fp中,尽管可以做到,但它不是标准。另一个字符。是按层次结构方式(大多数情况下具有继承性)的数据和类型系统的组织,以统治抽象和多态性。也许对于fp来说,这种场景是可能的,但并不受欢迎。
jneira

正交性不仅意味着没有冲突。这也意味着一个并不暗示另一个。
Deduplicator

12

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

这意味着它们不会互相影响。也就是说,功能性语言的功能不是很弱,因为它也是面向对象的。

他们似乎是相反的。它如何影响它们的正交性?

如果它们是相反的(即,纯粹的功能性语言不可能是面向对象的),那么根据定义它们将不是正交的。但是,我认为情况并非如此。

OO似乎是为状态和突变而构建的(命令式编程的稍微组织化的版本?)。而且我确实意识到对象可以是不可变的。但是OO似乎意味着对我声明/更改。

尽管对于大多数主流的OO语言都是如此,但是没有理由OO语言需要具有可变状态。

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


4
面向对象的语言不需要划分类/对象:Self,JavaScript,Io,Cecil和其他(数十种)其他语言很好地说明了这一点。
输入正确的意见,2010年

@JUST:好的,我删除了对类的引用。但是,公平地说,我没有说他们做到了。我只是说,如果它们确实具有这些功能,那么它们就是OO。我的意思是说“如果且仅当”。
sepp2k

7

对于两个概念来说,正交意味着它们可以在任何给定的表现形式中以任意程度独立实现。例如,考虑音乐,您可以按照音乐的谐音和节奏来对音乐作品进行分类。两个概念“谐音”和“节奏”在存在谐音和节奏乐曲,不谐音和有节奏乐曲,也有谐音和节奏乐曲以及谐和心律乐曲的意义上是正交的。

应用于原始问题,这意味着存在纯粹的功能性,非面向对象的编程语言(例如Haskell),纯粹的面向对象的“非功能性”语言(例如Eiffel),以及既非C语言又属于非语言的语言。两者都如Scala。

简而言之,Scala是面向对象的,这意味着您可以定义数据结构(“类”和“特征”),这些数据结构使用操作该数据的方法来封装数据,从而确保这些结构(“对象”)的实例始终位于定义状态(对象的合同在其类中列出)。

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

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

Scala实际上不需要您使用一种或另一种样式。它使您可以选择两全其美来解决问题。


7

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

因此,有时“面向对象”语言在采用“功能”编程语言的功能和理念时会取得成功,反之亦然。但是肯定不是所有的编程语言功能和理念都兼容。

例如,像OCaml这样的功能语言通过词汇作用域和闭包来完成封装,而面向对象的语言则使用公共/私有访问修饰符。这些本身并不是不兼容的机制,但是它们是多余的,像F#这样的语言(一种主要是功能性语言,旨在与明确的面向对象的.NET库和语言栈和谐地生活)必须竭尽所能差距。

作为另一个示例,OCaml将结构类型系统用于面向对象,而大多数面向对象的语言都使用名义类型系统。这些几乎是不兼容的,并且有趣地表示了面向对象语言领域内的不兼容。


1
有趣的一点。尽管OCaml是我的基本功(也是将对象导向与Hindley-Milner类型推断混合在一起的“第一”语言),但我还是避开了任何直接比较。如果没有的话,我也会提到Caml Special Light的模块系统,它比OCaml早了两年,并提供了与面向对象竞争的抽象/封装机制(它们试图以不同的方式解决类似的问题)。
Pascal Cuoq 2010年

@Pascal Cuoq-很高兴您发现这很有趣,我一直被分类为人造结构的概念迷住了,不止一次因为将植物列为动物而受到惩罚。
Stephen Swensen 2010年

应当注意,F#直接基于OCaml。
Jacobs Data Solutions

6

对象的想法可以以不变的方式实现。一个例子是《对象理论》Abadi和Cardelli,旨在将这些思想形式化,并且首先为对象赋予了不可变的语义,因为这使面向对象程序的推理变得更加简单。

在这种情况下,本来可以原地修改对象的方法将返回一个新对象,而先前的对象将保留下来。


5

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

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

您在谈论函数式编程。

而OO似乎是为状态和突变而构建的

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

像Scala这样的语言使同时进行OO和FP变得容易

并不是的。缺少尾部调用优化意味着大多数惯用的纯功能代码将在Scala中堆栈溢出,因为它会泄漏堆栈帧。例如,连续传递样式(CPS)和《关于》一书中描述的所有技术都是布鲁斯·麦卡丹(Bruce McAdam)总结的。没有简单的方法可以解决此问题,因为JVM本身无法进行尾部调用优化。

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


3
-1。您真的不需要在这里提出Scala-TCO问题。您不会留下任何机会来抨击Scala,是吗?
missingfaktor

7
@missingfaktor:TCO显然是他所说的Scala使FP变得容易的反例。面对大量相反的证据,您不会留下一个盲目提倡Scala的机会,是吗?
JD

5
毫不犹豫地大胆地使用“多数”这个词。即使是在纯函数式语言中,大部分循环也不是通过显式递归来完成的,而是通过调用Scala可以很好地提供的那种高阶函数来完成的。
戴夫·格里菲斯

5
太多年来,读写大量的功能代码。尾递归是在地图/ zip /扫描/折叠/缩小/等不起作用时被拉出的锤子。这并不奇怪。毕竟,这些调用存在的原因是要抽象出尽可能多的通用递归模式。我唯一想到的例外(尾部调用是首选解决方案)是Erlang的“将可变组件状态作为尾部调用args进行伪造”的技巧。
戴夫·格里菲斯

3
除非我有误解,否则在任何情况下,对列表折叠的调用都不会在树形折叠的尾部位置。无论如何,我平均每个月可能会在Scala遇到一次电话尾声问题,可能更少。我只是觉得这没什么大不了的,更不用说严重的缺陷了。也许您需要更严格地处理依赖关系图。
戴夫·格里菲斯


1

我刚刚找到了关于OOP和FP正交性的绝妙解释

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

假设表达式是 (1 + 2) * 3。那么AST将是:

       乘法
         / \
     加法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中,我们将按实现此表-我们将为每个动词编写一个函数,并且该函数对不同类型的参数会有不同的反应(可能使用模式匹配)。


该解释似乎是在争论FP和OOP不是正交的,而是实际上是相反的,互斥的,包罗万象的方法。
内森·塔吉

1
我有点直觉上喜欢“正交”。当然,从数学上来说并不是独立的。从某种意义上说,它们采用相反的,图形正交的方法来分解问题。
丹妮拉·皮亚托夫

它在数学上是适当的(因为工作台的轴垂直的),但是不幸的是,在某种意义上,没有人使用它来比较编程技术。因此,这是一个词源错误的情况。
内森·塔吉

-1

正交的。这听起来不错。如果您接受了教育,则可以将其带一点并假装。它有点像范例。

这完全取决于您进入哪个圈子以及每种编程技术将为您提供什么。我已经阅读了几篇有关SS的文章,大多数来自通常使用函数式编程语言的人都坚持认为,您只能进行函数式编程,而其他任何事情都与思维和思维方式背道而驰。

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

功能编程技术引起了人们极大的兴趣,这主要是由于处理多核芯片和并行编程时需要状态管理。目前看来,函数式编程确实在处理此问题上占了上风,但是您可以使用对象来实现相同的效果。您只是以不同的方式考虑问题。不用费神费力地尝试尽可能摆脱状态,而是查看设计中的对象,并了解如何使用设计模式,CRC和对象分析。但是,对象确实存在于何处,而功能编程要困难得多的地方是分析现实世界并将其映射到可理解的计算机化系统。以OO为例 一个人对象将是通过对人状态起作用的方法对状态的封装。在函数式编程中,一个人将分解为对人数据起作用的数据部分和功能,并附带一个附加条件,即数据应仅创建一次且不可变。

我必须承认,尽管来自面向对象的背景,但在大多数面向对象的语言中,当处理多核芯片时,我已经走了功能路线,主要是通过核心编程设计结构(例如线程和委托)并在其周围传递伪数据对象。这使我对OO编程技术提出了质疑,因为它似乎无法很好地映射到该线程设计。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.