ChatGPT解决这个技术问题 Extra ChatGPT

为什么函数式编程还没有被接管?

锁定。这个问题及其答案被锁定,因为这个问题离题但具有历史意义。它目前不接受新的答案或交互。

我已经阅读了一些关于声明式/函数式编程(语言)的文章,尝试过 Haskell 以及自己编写的一篇。从我所见,函数式编程比经典的命令式编程有几个优点:

无状态程序;无副作用

并发;与新兴的多核技术配合得非常好

程序通常更短,在某些情况下更易于阅读

生产力提高(例如:Erlang)

命令式编程是一个非常古老的范例(据我所知),可能不适合 21 世纪

为什么使用函数式语言或编写程序的公司仍然如此“罕见”?

为什么在查看函数式编程的优势时,我们仍在使用命令式编程语言?

也许在 1990 年还为时过早,但今天呢?

注意:这个问题已经在 meta 上至少讨论过两次,共识是应该保留它,尽管通过上面提到的“历史意义”锁存档。我强烈鼓励任何看到这个并着眼于解锁它的人停下来感谢你不必参与另一个关于这个问题的乏味讨论,享受他们的答案,并继续他们的业务。

S
Sarah Vessels

因为所有这些优点也是缺点。

无状态程序;无副作用

现实世界的程序都是关于副作用和突变的。当用户按下按钮时,是因为他们想要某事发生。当他们输入某些内容时,他们希望该状态取代曾经存在的任何状态。当会计部门的简·史密斯结婚并改名为简·琼斯时,支持打印她的工资支票的业务流程的数据库最好全部用于处理这种突变。当你用机关枪对着外星人开火时,大多数人不会在脑海中将其视为建造一个生命值较低的新外星人。他们将其建模为现有外星人属性的突变。

当编程语言概念从根本上与被建模的领域相矛盾时,很难证明使用该语言是合理的。

并发;与新兴的多核技术配合得非常好

问题只是被推来推去。使用不可变数据结构,您可以以可能处理陈旧数据为代价获得廉价的线程安全性。使用可变数据结构,您可以始终处理新数据,但代价是必须编写复杂的逻辑以保持数据的一致性。这并不像其中一个明显比另一个更好。

程序通常更短,在某些情况下更易于阅读

除非它们更长且更难阅读。学习如何阅读以函数式风格编写的程序是一项艰巨的技能。人们似乎更善于将程序设想为一系列要遵循的步骤,就像一个食谱,而不是一系列要执行的计算。

生产力提高(例如:Erlang)

为了证明雇用知道如何以函数式编程的程序员的巨额费用,生产力必须提高很多。

记住,你不想丢掉一个工作系统;大多数程序员不是从头开始构建新系统,而是维护现有系统,其中大部分是用非函数式语言构建的。想象一下,试图向股东证明这一点。为什么你要放弃现有的工作工资系统,以花费数百万美元建造一个新系统? “因为函数式编程很棒”不太可能让股东高兴。

命令式编程是一种非常古老的范式(据我所知),可能不适合 21 世纪

函数式编程也很古老。我不明白这个概念的时代是如何相关的。

不要误会我的意思。我喜欢函数式编程,我加入这个团队是因为我想帮助将函数式编程的概念带入 C#,并且我认为不可变风格的编程是未来的方式。但是,不能简单地希望以函数式编程方式进行编程需要付出巨大的代价。在几十年的时间里,向更实用的风格转变将缓慢而逐渐地发生。它将是这样的:向更实用的风格转变,而不是全面拥抱 Haskell 的纯洁和美丽并放弃 C++。

我以构建编译器为生,我们肯定会接受下一代编译器工具的函数式风格。这是因为函数式编程从根本上很好地解决了我们面临的各种问题。我们的问题都是关于获取原始信息——字符串和元数据——并将它们转换成不同的字符串和元数据。在发生突变的情况下,比如有人在 IDE 中打字,问题空间固有地适用于功能技术,例如仅增量重建树中发生变化的部分。许多域没有这些使它们明显适合功能样式的良好属性。


“当会计部门的简·史密斯结婚并改名为简·琼斯时,支持打印她的工资支票的业务流程的数据库最好全部用于处理这种突变。”将记录简·史密斯的旧名,我们不会追溯将简的旧名的所有实例更新为她的新名;)
@朱丽叶:当然。我的观点是,如果您有一个代表员工的对象,那么将“更改员工姓名”的操作视为代表员工的对象的突变而不改变对象身份是有意义的。当 Jane Smith 更改她的名字时,您不会创建一个名为 Jane Jones 的不同员工,否则该员工是相同的。没有两个员工有两个不同的名字。很自然地将这个过程建模为对象的突变,而不是构建新对象。
这是一个很好的答案,但我认为你有时夸大了你的情况。正如朱丽叶所说,虽然人们可能会认为这是改名,但实际上是更深层次的改名。尽管函数式程序对人们来说可能更难阅读(因为它是一种学习技能),但这通常不是因为它们更长。 Haskell 程序几乎总是比 Java 程序更简洁——即使在具有大量固有状态的“不合适”域中也是如此。
+1 读到这个答案真是一股清新的空气。很高兴能从您所在职位的某个人那里听到这种实用主义(对函数式编程有着潜在的热情)。
“因为所有这些优点也是缺点。无状态程序;没有副作用”:据我了解(我对 FP 了解得不够多,无法写出权威的答案)这是不正确的。函数式编程是关于引用透明性而不是避免状态(即使必须适当处理状态以确保引用透明性)。 Haskell 确实允许状态和突变。它只是提供了不同的(有人可能会争论,更好的)工具来推理它。
C
Community

Masterminds of Programming: Conversations with the Creators of Major Programming Languages

[Haskell] 为什么你认为没有函数式编程语言进入主流?约翰休斯:糟糕的营销!我不是说宣传;我们已经有很多了。我的意思是仔细选择要主导的目标市场利基市场,然后坚定地努力使函数式编程成为迄今为止解决该利基市场的最有效方法。在 80 年代快乐的日子里,我们认为函数式编程对一切都有好处——但称新技术“对一切都有好处”与称其“特别擅长什么”是一样的。品牌应该是什么?这是 John Launchbury 在 ICFP 受邀演讲中非常清楚地描述的一个问题。当 Galois Connections 的品牌是“功能语言的软件”时,他们几乎破产了,但自从专注于“高保证软件”以来,他们的实力越来越强。许多人不知道技术创新是如何发生的,并期望更好的技术会自行占据主导地位(“更好的捕鼠器”效应),但世界并非如此。


Haskell:20年后,一夜成名!
点击链接并阅读评论,了解 Grady Booch 的一个相当有趣的 dis。不知道 Booch 是谁,但无论如何它让我哈哈大笑。
Grady Booch 最终与 Jacobson 和 Rumbaugh 一起对 UML 的可憎之处负责。
G
G__

常见的答案是,两者都不会也不应该取代另一个——它们是不同的工具,具有不同的优缺点,哪种方法具有优势会因项目和其他“软”问题(如可用人才库)而异。

I think you're right that the growth of concurrency due to multi-core will increase the percentage (of the global set of development projects) when functional programming is chosen over other styles.

我认为今天这种情况很少见,因为今天的大多数专业人才库最熟悉命令式和面向对象的技术。例如,我不止一次选择 Java 作为商业项目的语言,因为它足够好、没有争议,而且我知道我永远不会用完可以用它(足够好)编程的人。


这是非常有见地和令人愉快的务实。绝对是我见过的所有形式的这个问题的最佳答案。
也许通过一种两者都是一等公民的语言会变得流行。
同意 110%。有几次,我试图进入 FP,但几周后我失去了继续下去的意愿。我已经在程序上编程了 30 多年,而且我太习惯于命令式编程。整个 IT 行业都是如此。改变不会来得容易也不会很快。
A
Anton Tykhyy

尽管函数式编程具有优势,但命令式和面向对象的编程永远不会完全消失。

命令式和面向对象的编程是对问题及其解决方案的逐步描述。因此,它可以更容易理解。函数式编程可能有点晦涩难懂。

最终,一个有用的程序总会有副作用(比如向用户提供实际输出以供消费),因此最纯粹的函数式语言仍然需要不时进入命令式世界的方法。

当前的最新技术是命令式语言(例如 C#)从函数世界(例如 lambda 语句)借用功能,反之亦然。


OOP 不是命令式编程的某种子集吗?
OOP 是一个超集。 OOP 之于命令式就像 C++ 之于 C。
我认为 OOP 不一定依赖于命令式编程。看看 Clojure 或 CLOS —— 两者都是功能性的,但都是面向对象的。
OO 语言往往是命令式的,但并非必须如此。 OCaml 是一种强大的(虽然不是纯粹的)函数式语言,其全部存在的理由都是面向对象的。
我不明白为什么 OOP 是命令式编程的超集。并非所有命令式代码都是 OOP,也并非所有功能代码都是非 OOP。我宁愿说 OOP 是命令式编程和函数式编程,就像空气动力机翼对于赛车、飞机、火箭、冷却风扇、风车或......一对一连接。
P
Peter Mortensen

不是吗?

Smalltalk 在当时是一个很棒的面向对象系统。为什么没有面向对象编程?嗯,它有。它只是看起来不像 Smalltalk。主流语言与 C++、Java、C# 等变得更像 Smalltalk .

函数式也是一样。 Haskell 是一门很棒的函数式语言。但是,与 20 年前相比,今天我们有更多的主流程序员使用类 C 语法。所以它必须看起来像 C。完成:查看任何 LINQ 表达式并告诉我它不起作用。


有趣的一点,但是主流语言是如何变得更像 Smalltalk 的呢?例如,C++、Java 和 C# 不基于消息发送,这(我相信)是 Smalltalk 范式中最重要的部分。
Jonathan:选择任何 Smalltalk 功能,观察它在 C++ 中最弱(最古老的),在 Java 中还可以,在 C# 中更好。例如,GC(仅适用于 Java/C#)、自动装箱(仅适用于 Java/C#)、闭包(仅适用于 C#)和反射(在 Java 中存在,在 C# 中更好)。如果您想要消息传递,请查看 C# 4 的 dynamic。这是这些功能中最符合 Smalltalk 的,所以我毫不奇怪它只出现在这三种语言中最现代的最新版本中。 :-)
Javascript、python 和 ruby 很好地说明了它的实际用途
P
Peter Mortensen

我相信命令式语言更普遍,因为这是更多人习惯的。函数式编程和命令式编程模型都不比另一个更晦涩或学术。事实上,它们是互补的。

一位发帖人说,命令式代码比函数式编程代码更容易理解。这只有在读者已经看过命令式代码的情况下才是正确的,特别是如果前面的示例属于同一个“家族”(例如,C/C++、Perl、PHP 和 Java)。我不会声称任何命令式语言都是如此。拿Java和Forth做个比较,举个极端的例子。

对于外行来说,所有编程语言都是难以理解的胡言乱语,除了 Hypertalk 和 SQL 等冗长的语言。 (值得注意的是,SQL 是一种声明性和/或函数式语言,并且非常受欢迎。)

如果我们从一开始就接受过 Lisp-y 或 Haskell-y 语言的培训,我们都会认为函数式编程语言是完全正常的。


“一位发帖人说命令式代码比函数式编程代码更容易理解。只有读者已经看过命令式代码时才会这样说,特别是如果前面的例子属于同一个“家族”(例如,C/C++, Perl、PHP 和 Java)。”:非常正确(+1):我记得当我开始编程时,我必须付出多少努力来学习 Pascal 和 C。既然我对这些语言有了一些经验,那么阅读 Scala、Haskell 或 Scheme 是多么容易。
有时我仍然认为可变状态有用的唯一原因是它提供了一种编写快速代码的简单方法(无需复制);但原因可能是我对函数式编程了解得不够多,而且在 90% 的情况下,您可以在不使用可变状态的情况下编写快速的函数式代码。
用于组织计算的最广泛使用和长期存在的环境之一是电子表格。本质上是一个带有单元而不是命名变量的函数式编程环境。我不相信人们,一般来说,天生就将程序设想为一系列步骤。可能是沉浸在广泛使用的命令式语言中的程序员。
J
Jerry Coffin

你已经得到了足够多的答案,我将只提及一些我还没有看到的事情。

首先,(在我看来)最重要的是,程序语言从它们的通用性中受益匪浅。例如,几乎任何了解几乎任何主流过程(或 OO)语言到几乎任何程度的人都可以相当好地阅读其他大多数语言。我积极避免使用 Java、C#、Cobol、Fortran 或 Basic(仅举几个例子),但可以很好地阅读它们中的任何一个——事实上,几乎和每天使用它们的人一样好。

在功能方面,这是不正确的。举个例子,我也可以相当合理地编写 Scheme,但这在阅读 Ocaml 或 Haskell 时用处不大(仅举几个例子)。即使在一个家族中(例如,Scheme vs.,Common Lisp),对一个家族的熟悉似乎也不能很好地转化为另一个家族。

函数式代码更具可读性的说法往往仅在狭窄范围的条件下才是正确的。对于非常熟悉该语言的人来说,可读性确实非常好——但对于其他所有人来说,它通常几乎不存在。更糟糕的是,虽然过程语言的差异主要在于语法,因此相对容易学习,但函数语言的差异往往更为根本,因此需要大量研究才能真正理解它们(例如,了解 Lisp 对理解 Monad 帮助不大)。

另一个主要观点是,函数式程序比过程式程序更短的想法通常更多地基于语法而不是语义。用 Haskell 编写的程序(例如)通常很短,但它的功能性只是其中的一小部分。如果仅仅是 Haskell 具有相对简洁的语法,那就太好了。

很少有纯函数式语言可以在简洁的源代码方面与 APL 竞争(不过,公平地说,APL 也支持创建更高级别的函数,因此与其他一些情况相比,这并没有太大的区别)。相反,Ada 和 C++(仅举几个例子)在完成给定任务所需的操作数量方面可能相当有竞争力,但语法(至少通常)要冗长得多。


优秀的评论!我完全同意。我发现大多数程序语言都相当容易阅读和理解,尽管我只是其中几个方面的真正专家。不能对 FP 语言说同样的话。
另一个重要的一点是,在任何给定的范式中,你都会发现从新手到大师的一系列专业知识。 FP 代码对于专家来说可能很容易阅读和理解,但中级程序员可能仍然难以理解。专家通常只占 FP 社区的一小部分。
M
Mark Harrison

无感知需求

我记得我的老上司 Rick Cline 在我给他看 John Backus 的图灵奖演讲的副本时的反应,题为 Can Programming Be Libered from the von Neumann Style?

他的回答是:“也许我们中的一些人不想从冯诺依曼风格中解放出来!”


J
J D

为什么函数式编程还没有被接管?

功能性对某些事情更好,对其他事情更糟,因此它永远不会“接管”。虽然它在现实世界中已经无处不在。

无状态程序;无副作用

无状态程序更容易测试。现在,这在工业中得到了广泛的赞赏并经常被利用。

并发;与新兴的多核技术相得益彰程序通常更短,在某些情况下更易于阅读生产力提高(例如:Erlang)

您将并发和并行性混为一谈。

使用通信顺序进程 (CSP) 可以有效地完成并发。 CSP 中的代码可以改变其本地状态,但它们之间发送的消息应该始终是不可变的。

纯函数式编程在多核上的表现非常糟糕,因为它对缓存非常不友好。核心最终争夺共享内存,并行程序无法扩展。

为什么使用函数式语言或编写程序的公司仍然如此“罕见”?

Scala 通常被认为是一种函数式语言,但它并不比 C# 更实用,C# 是当今世界上最流行的语言之一。

为什么在查看函数式编程的优势时,我们仍在使用命令式编程语言?

纯函数式编程有很多严重的缺点,所以我们使用不纯的函数式语言,如 Lisp、Scheme、SML、OCaml、Scala 和 C#。


L
Lance Roberts

当我想到函数式编程可能会给我的工作项目带来什么时,我总是被引导到相同的思路上:

要获得函数式编程的全部优势,您需要惰性。是的,有严格的函数式语言,但函数式编程的真正好处在严格的代码中并不那么出色。例如,在 Haskell 中,很容易在列表上创建一系列惰性操作并将它们连接起来并将它们应用于列表。例如。 op1 $ op2 $ op3 $ op4 $ someList。我知道它不会构建整个列表,并且在内部我只会得到一个很好的循环,一次遍历一个元素。这使您可以编写真正模块化的代码。两个模块之间的接口可能涉及移交潜在的庞大数据结构,但您不必让结构常驻。但是当你有懒惰时,就很难推断内存使用情况。更改 Haskell 编译器标志会经常将算法使用的内存量从 O(N) 更改为 O(1),但有时不会。当您的应用程序需要最大限度地利用所有可用内存时,这是不可接受的,即使对于不需要所有内存的应用程序也不是很好。


懒惰与调试的交互也不尽如人意。
当我发现我在其他语言中追逐的许多错误都与缺乏引用透明度有关时,我不太担心调试问题,即使它们有时会很痛苦。
P
Phil

两件事情:

无论技术有多好,都需要时间。 FP 背后的想法大约有 70 年的历史。但它在软件工程中(在战壕中,在工业中)的主流使用可能不到 10 年。要求开发人员采用全新的思维方式是可能的,但这需要时间(很多很多年)。例如,OOP 在 1980 年代初期确实得到了主流使用。然而,它直到 1990 年代后期才占据主导地位。你需要人们在它大受欢迎之前被迫面对它的力量。目前,人们正在使用不利用并行性的工具,并且一切正常。当不使用并行的应用程序变得难以忍受时;那么很多人将被迫使用并行工具,而 FP 可能会迅速流行起来。这也可能适用于 FP 的其他优势。


FP 非常擅长代码重用。可能比OO更好。我不得不在工作中处理它几次,迁移到不同的类型,以及一个新的系统,而且它很轻松。
@Freddy Rios 和 @nlucaroni。我改写了评论以消除误解。