我一次又一次听到,我正在尝试理解和验证FP和OO是正交的。
首先,两个概念正交是什么意思?
FP尽可能地鼓励不变性和纯净性,而OO似乎是为状态和突变而构建的–命令命令编程的组织性略微?我意识到对象可以是不可变的,但是OO似乎暗示着我的状态/变化。
他们似乎是相反的。这如何影响它们的正交性?
像Scala这样的语言可以轻松实现OO和FP,这是否会影响这两种方法的正交性?
Answers:
正交性意味着两件事是不相关的。它来自数学,它的意思是垂直的。通常,这可能意味着两个决策无关,或者在考虑另一个主题时一个主题无关紧要。如此处所使用的,正交意味着一个概念既不暗示也不排除另一个。
面向对象编程和函数编程这两个概念并不互不兼容。面向对象并不意味着可变性。许多以传统方式介绍给面向对象程序的人通常会首先使用C ++,Java,C#或类似的语言,在这些语言中,可变性是常见的,甚至受到鼓励(标准库提供了多种可变类供人们使用)。因此,可以理解的是,许多人将面向对象的编程与命令式编程和可变性相关联,因为这是他们学会的方法。
但是,面向对象的编程涵盖了以下主题:
这些都不意味着可变性,也不排除功能编程。是的,它们是正交的,因为它们是不同的概念。它们不是对立的-您可以使用其中一种,也可以使用两者(甚至都不用)。诸如Scala和F#之类的语言试图将这两种范例组合成一种语言:
Scala是一种多范式编程语言,旨在集成面向对象编程和函数式编程的功能。
F#是.NET的简洁,表达性强,高效的功能和面向对象的语言,可帮助您编写简单的代码来解决复杂的问题。
首先,两个概念正交是什么意思?
这意味着这两个概念没有相反的思想或彼此不兼容。
FP尽可能地鼓励不变性和纯度。OO似乎是为状态和突变而构建的(命令式编程的稍微组织化的版本?)。而且我确实意识到对象可以是不可变的。但是OO似乎意味着对我声明/更改。
他们似乎是相反的。它如何影响它们的正交性?
像Scala这样的语言可以轻松实现OO和FP两者,这是否会影响这两种方法的正交性?
OO涉及封装,对象组成,数据抽象,通过子类型进行的多态性以及必要时的受控突变(OO中也鼓励不变性)。FP与功能组合,控件抽象和约束多态性(又称参数多态性)有关。因此,这两个想法并不矛盾。它们都为您提供了不同种类的功能和抽象机制,这在一种语言中肯定是可能的。实际上,这就是Scala构建的基础!
在Google的Scala实验演讲中,马丁·奥德斯基(Martin Odersky)很好地解释了他如何相信OO和FP这两个概念彼此正交,以及Scala如何将这两种范式优雅地,无缝地结合成一个新的范式,在Scala社区中广为人知。对象功能范式。必须看着你说话。:-)
首先,两个概念正交是什么意思?
这意味着它们不会互相影响。也就是说,功能性语言的功能不是很弱,因为它也是面向对象的。
他们似乎是相反的。它如何影响它们的正交性?
如果它们是相反的(即,纯粹的功能性语言不可能是面向对象的),那么根据定义它们将不是正交的。但是,我认为情况并非如此。
OO似乎是为状态和突变而构建的(命令式编程的稍微组织化的版本?)。而且我确实意识到对象可以是不可变的。但是OO似乎意味着对我声明/更改。
尽管对于大多数主流的OO语言都是如此,但是没有理由OO语言需要具有可变状态。
如果一种语言具有对象,方法,虚拟继承和即席多态性,那么它就是一种面向对象的语言-无论它是否也具有可变状态。
对于两个概念来说,正交意味着它们可以在任何给定的表现形式中以任意程度独立实现。例如,考虑音乐,您可以按照音乐的谐音和节奏来对音乐作品进行分类。两个概念“谐音”和“节奏”在存在谐音和节奏乐曲,不谐音和有节奏乐曲,也有谐音和节奏乐曲以及谐和心律乐曲的意义上是正交的。
应用于原始问题,这意味着存在纯粹的功能性,非面向对象的编程语言(例如Haskell),纯粹的面向对象的“非功能性”语言(例如Eiffel),以及既非C语言又属于非语言的语言。两者都如Scala。
简而言之,Scala是面向对象的,这意味着您可以定义数据结构(“类”和“特征”),这些数据结构使用操作该数据的方法来封装数据,从而确保这些结构(“对象”)的实例始终位于定义状态(对象的合同在其类中列出)。
另一方面,Scala是一种功能语言,这意味着它倾向于不可变而不是可变状态,并且函数是一类对象,可以像使用任何其他对象一样将其用作其他函数的局部变量,字段或参数。除此之外,Scala中几乎每个语句都有一个值,这鼓励您使用函数式编程风格。
Scala另外,面向对象编程和函数式编程的正交性意味着,作为程序员,您可以自由选择自己认为适合的这两个概念的任何组合。您可以以纯命令式的方式编写程序,仅使用可变对象,而根本不使用函数作为对象,另一方面,也可以在Scala中编写纯功能性程序,而不使用其任何面向对象的功能。
Scala实际上不需要您使用一种或另一种样式。它使您可以选择两全其美来解决问题。
像所有分类一样,将编程语言分为功能性,面向对象,过程性等是虚构的。但是我们确实需要分类,在编程语言中,我们通过一组语言功能和使用该语言的人的哲学方法(在后者受前者影响的情况下)进行分类。
因此,有时“面向对象”语言在采用“功能”编程语言的功能和理念时会取得成功,反之亦然。但是肯定不是所有的编程语言功能和理念都兼容。
例如,像OCaml这样的功能语言通过词汇作用域和闭包来完成封装,而面向对象的语言则使用公共/私有访问修饰符。这些本身并不是不兼容的机制,但是它们是多余的,像F#这样的语言(一种主要是功能性语言,旨在与明确的面向对象的.NET库和语言栈和谐地生活)必须竭尽所能差距。
作为另一个示例,OCaml将结构类型系统用于面向对象,而大多数面向对象的语言都使用名义类型系统。这些几乎是不兼容的,并且有趣地表示了面向对象语言领域内的不兼容。
对象的想法可以以不变的方式实现。一个例子是《对象理论》Abadi和Cardelli,旨在将这些思想形式化,并且首先为对象赋予了不可变的语义,因为这使面向对象程序的推理变得更加简单。
在这种情况下,本来可以原地修改对象的方法将返回一个新对象,而先前的对象将保留下来。
您可以将函数实现为对象,而对象可以实现为函数的集合,因此这两个概念之间显然存在某些关系。
FP尽可能地鼓励不变性和纯度
您在谈论纯函数式编程。
而OO似乎是为状态和突变而构建的
不需要对象是可变的。我会说对象和变异是正交的概念。例如,OCaml编程语言提供了用于纯功能对象更新的语法。
像Scala这样的语言使同时进行OO和FP变得容易
并不是的。缺少尾部调用优化意味着大多数惯用的纯功能代码将在Scala中堆栈溢出,因为它会泄漏堆栈帧。例如,连续传递样式(CPS)和《关于》一书中描述的所有技术都是布鲁斯·麦卡丹(Bruce McAdam)总结的。没有简单的方法可以解决此问题,因为JVM本身无法进行尾部调用优化。
关于纯函数式程序设计和面向对象程序设计的正交性,我想说它们至少接近于正交,仅仅是因为纯函数式程序设计仅处理小程序(例如高阶函数),而面向对象程序设计则处理大型程序。程序的大规模结构化。这就是为什么函数式编程语言通常会提供其他一些用于大规模结构化的机制的原因,例如,标准ML和OCaml的高阶模块系统,Common Lisp的CLOS或Haskell的类型类。
有一两件事让我明白FP和面向对象之间的关系是SICP书,特别是部分“功能程序和对象的模块化的模块化”如果你正在思考这些问题,你有空闲的周末,它可能是通过值得一读前三章,其漂亮的眼神。
我刚刚找到了关于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中,我们将按列实现此表-我们将为每个动词编写一个函数,并且该函数对不同类型的参数会有不同的反应(可能使用模式匹配)。
正交的。这听起来不错。如果您接受了教育,则可以将其带一点并假装。它有点像范例。
这完全取决于您进入哪个圈子以及每种编程技术将为您提供什么。我已经阅读了几篇有关SS的文章,大多数来自通常使用函数式编程语言的人都坚持认为,您只能进行函数式编程,而其他任何事情都与思维和思维方式背道而驰。
面向对象的编程主要是关于捕获状态并使该状态尽可能地局部化,以免受到不属于您管理状态的对象的任何部分的影响。另一方面,函数式编程从不同的角度看待状态问题,并尝试将状态与系统分离并将其简化为函数。是的,您可以在代码中同时使用这两种技术,但是它们都从不同角度考虑了软件设计。
功能编程技术引起了人们极大的兴趣,这主要是由于处理多核芯片和并行编程时需要状态管理。目前看来,函数式编程确实在处理此问题上占了上风,但是您可以使用对象来实现相同的效果。您只是以不同的方式考虑问题。不用费神费力地尝试尽可能摆脱状态,而是查看设计中的对象,并了解如何使用设计模式,CRC和对象分析。但是,对象确实存在于何处,而功能编程要困难得多的地方是分析现实世界并将其映射到可理解的计算机化系统。以OO为例 一个人对象将是通过对人状态起作用的方法对状态的封装。在函数式编程中,一个人将分解为对人数据起作用的数据部分和功能,并附带一个附加条件,即数据应仅创建一次且不可变。
我必须承认,尽管来自面向对象的背景,但在大多数面向对象的语言中,当处理多核芯片时,我已经走了功能路线,主要是通过核心编程设计结构(例如线程和委托)并在其周围传递伪数据对象。这使我对OO编程技术提出了质疑,因为它似乎无法很好地映射到该线程设计。