为什么还没有接受函数式编程呢?

pan*_*rax 197 functional-programming

我已经阅读了一些关于声明/函数编程(语言)的文章,尝试过Haskell以及自己编写的文本.从我所看到的,函数式编程比传统的命令式风格有几个优点:

  • 无国籍计划; 无副作用
  • 并发; 采用不断增长的多核技术,发挥得非常好
  • 程序通常较短,在某些情况下更容易阅读
  • 生产力提高(例如:Erlang)

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

为什么使用功能语言的公司或程序仍然如此"罕见"?

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

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

Eri*_*ert 530

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

无国籍计划; 无副作用

真实世界的计划都是关于副作用和变异的.当用户按下按钮时,因为他们想要发生某些事情.当他们输入某些内容时,他们希望该状态替换曾经存在的状态.当Jane Smith在会计中结婚并将她的名字改为Jane Jones时,支持打印她的薪水的业务流程的数据库最好是处理那种突变.当你向外星人发射机枪时,大多数人并没有在精神上建模那个具有较少生命值的新外星人的建造; 他们将其模型化为现有外星人属性的变异.

当编程语言概念从根本上反对被建模的域时,很难证明使用该语言是正确的.

并发; 采用不断增长的多核技术,发挥得非常好

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

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

除非它们更长且更难阅读.学习如何阅读以功能风格编写的程序是一项艰巨的任务; 人们似乎更善于将程序设想为一系列要遵循的步骤,比如一个配方,而不是一系列要进行的计算.

生产力提高(例如:Erlang)

为了证明招聘那些知道如何以功能方式编程的程序员的大量费用,生产力必须上升很多.

记住,你不想丢掉一个工作系统; 大多数程序员不是从头开始构建新系统,而是维护现有系统,其中大多数都是用非功能语言构建的.想象一下,试图向股东证明这一点.为什么要废弃现有的工作薪资系统,以数百万美元的价格建造一个新工资?"因为功能编程很棒"不太可能令股东高兴.

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

功能编程也很老.我不知道这个概念的年龄是如何相关的.

别误会我的意思.我喜欢函数式编程,我加入了这个团队,因为我想帮助将函数式编程的概念引入C#,我认为以不可变的风格编程是未来的方式.但是,功能风格的编程需要付出巨大的代价,而不能简单地将其抛弃.在几十年的时间里,向更具功能性的风格转变将逐渐缓慢地发生.这就是它的本质:向更具功能性的风格转变,而不是批判性地接受Haskell的纯洁和美丽以及放弃C++.

我以构建编译器为生,我们绝对拥抱下一代编译器工具的功能风格.那是因为函数式编程从根本上说是我们遇到的各种问题的良好匹配.我们的问题都是关于获取原始信息 - 字符串和元数据 - 并将它们转换为不同的字符串和元数据.在发生突变的情况下,就像有人在IDE中输入一样,问题空间固有地适用于功能性技术,例如逐步重建仅改变树的部分.许多域没有这些很好的属性,使它们显然适合功能样式.

  • "当Jane Smith在会计中结婚并将她的名字改为Jane Jones时,支持打印她的薪水的业务流程的数据库最好是处理那种突变." 将会有Jane Smith以前的名字记录,我们不会追溯将Jane的原名所有实例更新为她的新名字;) (41认同)
  • @Juliet:当然.我的观点是,如果您有一个代表员工的对象,那么将"更改员工姓名"的操作视为代表员工*的对象的变异是有意义的,该对象不会改变对象身份*.当Jane Smith改变她的名字时,你不会创建一个名为Jane Jones的*不同*员工.没有两名员工有两个不同的名字.将此过程建模为对象的变异是很自然的,而不是构造新对象. (40认同)
  • +1阅读这个答案是一种新鲜空气.非常棒的听到你职位上的某些人的实用主义(对函数式编程有潜在的热情). (29认同)
  • 这是一个很好的答案,但我认为你有时夸大你的情况.就像朱丽叶所说的那样,虽然人们可能会认为这是一个名称变更,但它确实是更深层次的名称替代品.虽然功能性程序可能更难以阅读(因为它*是一种学习技能),但这通常不是因为它们更长.Haskell程序几乎总是比Java程序更简洁 - 即使在具有大量固有状态的"不合适"域中也是如此. (24认同)
  • "因为所有这些优点也是缺点.无状态程序;没有副作用":据我所知(我不太了解FP写一个权威的答案)这是**不正确**.函数式编程是关于参照透明度而不是关于避免状态(即使必须适当处理状态以确保参考透明度).Haskell**允许**状态和变异.它只是提供了不同的(可以争辩的,更好的)工具来推理它. (11认同)
  • @Sheng:但是,这与我的观点无关.我们的语言具有变异和引用标识,因为*我们打算建模的域是具有变异和引用标识的域*.我们可以用函数式语言模拟这些东西吗?当然.我们能成功地用函数式语言编写程序来建模这些域吗?当然.但是当一种语言的习语自然地与建模域的特征相匹配时,开发人员更容易理解和维护从现实世界到模型的映射. (5认同)
  • @Chuck:难以阅读并不一定意味着更长.虽然Java是一种非常冗长的语言,但它可以编写它以使其读取接近简单的英语.我对函数式语言的体验是有限的,但在开始理解代码之前,我通常不得不改变思路并解析语法. (4认同)
  • 哲学的随机视角(特别是个人身份和自由意志的问题),我只是陈述而不是支持(很难用这个角色限制):当你射击那个外星人时,一个新的具有较少生命值的外星人确实会被归还.无论你是改变状态还是采用旧状态来返回一个新状态,它们都只是两种模拟同一事物的方式."同样的东西"是一种超越任何一种真实表现的抽象.因此,无论哪个人都可以更快地完成工作:)(抓住史诗般的帖子顺便说一下.我希望我能再次投票) (3认同)
  • @sheng我向你保证,名称变更不是合法的,因此对它们进行这样的建模可能是一个坏主意.不要被这个例子的细节所困扰.关键在于事情会发生变化,因此将其建模为保持身份的突变是有意义的. (3认同)
  • 看看F#的精彩设计,它融合了功能,并发,过程和OO样式,包括访问.NET BCL和非F#.NET程序集.如果F#严格来说是一种源自ML和OCaml的函数式语言,那么它在.NET领域的相关性,实用性和采用率就会低得多. (2认同)
  • @Gabe:好吧,FORTRAN,但仅仅一年左右. (2认同)
  • @Sheng:但这不是名字改变在婚姻和离婚方面的作用.并不是Jane在她的婚姻中有一个姓氏,而是另一个姓氏,然后在她离婚时又恢复到以前的名字.相反,婚姻,离婚和姓名变更都是单独的法律事件; 一个人只需在提交婚姻或离婚文件的同时提交法律名称变更文件.将其建模为先前状态的*reversion*是错误的; 这是两个突变. (2认同)
  • @UAvalos:我已多次说过,功能性技术使得编程的推理变得更容易,而功能性技术是驯服并发性问题的关键.但是,它不是灵丹妙药; 复杂的状态和复杂的控制流程最终会导致复杂的程序,无论使用何种样式.我相信我对函数式编程技术的理解相当不错,花了很多年时间将功能特性设计到编程语言中,并使用这些技术来构建编译器. (2认同)

Nic*_*kis 38

编程的主人:与主要编程语言的创建者的对话

[哈斯克尔]

为什么你认为没有函数式编程语言进入主流?

约翰休斯:营销不佳!我不是指宣传; 我们已经有了很多.我的意思是谨慎选择目标市场利基来支配,然后坚定不移地努力使功能性编程成为解决这一利基的最有效方式.在80年代的快乐时光中,我们认为函数式编程对一切都有好处 - 但是将新技术称为"对一切都有好处"就像称之为"特别好无所求"一样.该品牌应该是什么?这是John Launchbury在他在ICFP的邀请演讲中非常清楚地描述的一个问题.当他们的品牌是"功能语言软件"时,Galois Connections几乎陷入困境,但他们因为专注于"高保证软件"而不断壮大.

许多人不知道技术创新是如何发生的,并期望更好的技术本身就会成为主导("更好的捕鼠器"效应),但世界并不是那样.

  • 哈斯克尔:20年后,一夜成名! (36认同)
  • Grady Booch最终与Jacobson和Rumbaugh一起负责UML的憎恶. (4认同)

G__*_*G__ 27

股票的答案是既不会也不应该取代另一个 - 它们是具有不同优点和缺点的不同工具,哪种方法的优势取决于项目和其他"软"问题,如可用的人才库.

我认为你是正确的,当选择函数式编程而不是其他样式时,由多核引起的并发性增长将增加(全局开发项目集)的百分比.

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


Rob*_*vey 26

尽管功能编程具有优势,但命令式和面向对象编程永远不会完全消失.

命令式和面向对象编程是对问题及其解决方案的逐步描述.因此,可以更容易理解.功能编程可能有点模糊.

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

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

  • 我不认为OOP必然依赖于命令式编程.看一下Clojure或CLOS - 两者都是功能性的,但是面向对象的. (10认同)
  • OOP是一个超集.OOP是必不可少的,因为C++是C语言. (9认同)
  • 面向对象语言往往是必要的,但不一定非必要.OCaml是一种强有力的(虽然不是纯粹的)功能性语言,其完整的存在理由是OO. (9认同)
  • 我不明白为什么OOP是命令式编程的超集.并非所有命令性代码都是OOP,并非所有功能代码都是非OOP.我宁愿说OOP是命令式编程和功能编程,如空气动力学的翅膀是赛车,飞机,火箭,或冷却风扇,或风车,或......即,概念是相关的,但没有紧密耦合在1对1的连接. (7认同)
  • OOP不是命令式编程的某种子集吗? (3认同)

Ken*_*Ken 21

不是吗?

Smalltalk在当天是一个伟大的面向对象系统.为什么没有接管面向对象的编程?好吧,它有.它看起来不像Smalltalk.主流语言随着C++,Java,C#等变得越来越像Smalltalk.时尚和风格变化比任何东西都要慢,所以当OO成为主流时,我们通过将部分OO粘贴到旧语言上来获得它,所以看起来像C一样吞下去.

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

  • Jonathan:选择任何Smalltalk功能,观察它在C++中是最弱的(最老的),在Java中是好的,在C#中更好.例如,GC(仅限Java/C#),自动装箱(仅限后来的Java/C#),闭包(仅限C#)和反射(在Java中存在,在C#中更好).如果你想要消息传递,请看C#4的`dynamic`.这是这些功能中最小的一部分,因此我不会惊讶它只出现在这三种语言中最现代化的最新版本中.:-) (4认同)
  • 有趣的是,主流语言如何变得更像Smalltalk?例如,C++,Java和C#不是基于消息发送,我相信它是Smalltalk范例中最重要的部分. (2认同)

Bar*_*own 15

我认为命令式语言更为普遍,因为这是更多人习惯的.函数式编程和命令式编程模型都不比其他模式更模糊或更具学术性.事实上,它们是互补的.

一张海报说,命令式代码比函数式编程代码更容易理解.只有当读者已经看到命令性代码时才会这样,特别是如果先前的示例是同一"族"的一部分(例如,C/C++,Perl,PHP和Java).我不会声称任何命令式语言都是如此; 比较Java和Forth,做一个极端的例子.

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

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

  • 用于组织计算的最广泛使用且寿命最长的环境之一是电子表格; 本质上是一个带有单元而不是命名变量的函数式编程环 我不相信人们通常将程序本身设想为一系列步骤.也许,程序员沉浸在广泛的命令式语言中. (3认同)
  • 我仍然认为可变状态有用的唯一原因是它提供了一种编写快速代码的简单方法(无需复制); 但原因可能是我不知道足够的函数式编程,并且在90%的情况下,您可以编写快速的函数代码而不使用可变状态. (2认同)

Jer*_*fin 14

你已经得到了足够的答案,我只提到了一些我还没有提到的东西.

首先,(在我看来)最重要的是,程序语言从它们的共同程度中获益匪浅.举一个例子,几乎任何知道几乎任何主流程序(或OO)语言几乎任何程度的人都可以很好地阅读其他大多数语言.我主动避免使用Java,C#,Cobol,Fortran或Basic(仅举几个例子),但可以很好地阅读它们中的任何一个 - 实际上,就像每天使用它们的人一样.

在功能方面,这是真少.例如,我也可以非常合理地编写Scheme,但这在阅读Ocaml或Haskell时几乎没用(仅举几个例子).即使在一个单一的家庭中(例如,Scheme vs.,Common Lisp),对一个家庭的熟悉似乎并不能很好地转化为另一个家庭.

功能代码更具可读性的说法仅在狭窄的条件下才是真实的.对于那些对语言非常熟悉的人来说,可读性确实很好 - 但对于其他人来说,它通常几乎不存在.更糟糕的是,虽然程序语言的差异主要是语法,因此相对容易学习,但功能语言的差异往往更为基础,因此需要大量的学习才能真正理解(例如,知道Lisp对理解Monads没什么帮助).

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

很少有纯粹的函数式语言可以与APL很好地竞争简洁的源代码(尽管,公平地说,APL也支持创建更高级别的函数,因此与其他情况一样,这并没有太大差异).反之,阿达和C(仅几个例子)++可以在必要的完成给定任务操作的数量方面相当有竞争力,但语法(至少通常)基本上更详细.


Mar*_*son 11

没有感知的需要

我回忆起我的老Boss Rick Cline的回应,当时我向他展示了John Backus图灵奖的演讲稿题为" 可以从冯·诺依曼风格中解放出来的节目"吗?

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


Jon*_*rop 10

为什么还没有接受函数式编程呢?

功能对某些事情更好,对其他事情更糟,因此永远不会"接管".尽管如此,它已经在现实世界中无处不在.

无国籍计划; 无副作用

无状态程序更容易测试.现在,人们普遍认识到并经常在工业中加以利用.

并发; 使用不断增长的多核技术,效果非常好程序通常更短,在某些情况下更容易阅读生产力提升(例如:Erlang)

你在混淆并发和并行.

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

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

为什么使用功能语言的公司或程序仍然如此"罕见"?

Scala通常被认为是一种功能语言,但它并不比C#更具功能性,C#是当今世界上最流行的语言之一.

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

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


sig*_*fpe 7

当我想到函数式编程可能会给我的工作项目带来什么时,我总是沿着同样的思路走下去:

  1. 要获得函数式编程的全部优势,您需要懒惰.是的,有严格的函数式语言,但函数式编程的真正好处并没有在严格的代码中发挥作用.例如,在Haskell中,很容易在列表上创建一系列延迟操作并将它们连接起来并将它们应用到列表中.例如.op1 $ op2 $ op3 $ op4 $ someList.我知道它不会构建整个列表,而在内部我只是要获得一个很好的循环,一次一个地遍历元素.这允许您编写真正模块化的代码.两个模块之间的接口可能涉及移交潜在的大量数据结构,但您不必将结构驻留在其中.

  2. 但是当你有懒惰时,很难推断内存的使用.更改Haskell编译器标志经常会将算法使用的内存量从O(N)更改为O(1),有时则不会.当您的应用程序需要最大限度地利用所有可用内存时,这是不可接受的,即使对于不需要所有内存的应用程序也不是很好.

  • 当我发现我在其他语言中追逐的许多错误与缺乏引用透明度有关时,我不太担心调试问题,即使它们有时会很痛苦. (3认同)

Phi*_*hil 6

两件事情:

  1. 无论技术有多好,都需要时间.FP背后的想法已有70年历史.但它在软件工程(在战壕,工业中)的主流使用可能不到10年.要求开发人员采用种族新思维模式是可能的,但这需要时间(很多年).例如,OOP在20世纪80年代初确实成为主流用途.然而,直到20世纪90年代后期它才获得了宿舍.
  2. 你需要人们在遭遇技术之前被迫面对技术的力量.目前,人们正在使用不能利用并行性的工具,而且工作正常.当不使用并行性的应用变得难以忍受时; 然后很多人将被迫使用并行工具,FP可能会大受欢迎.这也可能适用于FP的其他优势.

  • FP非常适合代码重用.可能比OO更好.我不得不在工作中处理它几次,迁移到不同的类型,以及一个新的系统,它是无痛的. (3认同)