函数式编程正在上升吗?


42

我最近注意到功能编程语言越来越流行。我最近看到了Tiobe索引与去年相比如何显示出其受欢迎程度的增加,尽管根据该索引,其中大多数甚至都没有进入前50种最受欢迎​​的语言。

这已经有相当长的一段时间了。函数式编程根本没有像其他模型那样流行(即,面向对象的编程)。

但是,我已经看到了对函数式编程功能的浓厚兴趣,现在,多核越来越流行,开发人员开始对由Haskell和Erlang等语言在过去探索的其他并发模型表现出兴趣。

我非常感兴趣地看到,尽管缺乏社区的广泛认可,但越来越多的此类语言继续出现。Clojure(2007),Scala(2003),F#(2002)只是最近十年的三个例子。

我本人一直在花时间学习Haskell和Scala。尽管存在了这么长的时间,但对于我来说,这是一个全新的范例,我发现了巨大的潜力。

当然,我最大的问题是,这些方法中的任何一种是否将变得足够流行,以至于考虑对其进行任何努力,但这是一个问题,即使人们对此大惊小怪,连Mandrake都无法回答。

我想问的是:

  • 在哪种情况下,我应该考虑一种更适合执行给定任务的功能编程语言?除了最近流行的并行编程多核问题。
  • 如果我决定改用函数式编程语言,那您会认为这是我将要面临的最大陷阱?(除了范式更改和由于懒惰的评估而难以评估性能)。
  • 有这么多的函数式编程语言,您将如何选择一种更适合您的需求的语言?

任何进一步研究的建议都将受到欢迎。

我在网上搜索了一些意见,看来所有这些重新流行起来都来自这样的想法,即现在我们将要碰到摩尔定律,函数式编程语言将会出现并英勇地拯救我们。但是,如果是这样的话,我想说现有流行语言有更多的可能性适应这种范例。

你们当中有些人可能每天都有使用这些语言的丰富经验,可以为您提供有关该主题的更多见解。您的所有意见将得到更好的理解和认真考虑。

提前致谢!


4
是Erlang,不是Earlang(我已经编辑了您的帖子,但是系统不允许1个字母的编辑)。
quant_dev

6
值得一说的是-功能性语言和命令性语言之间没有界限。ML系列语言不是没有副作用的,并且支持命令式语句和结构以及可变变量。一些命令式语言-我最想念的是Python和Javascript-具有从函数式编程中获得的重要功能。我个人希望在主流使用中看到更多的功能性想法,尤其是模式匹配。
Steve314 2011年

1
具有功能语言共有的一些功能并不一定会使语言“功能化”。函数式编程与特定语言功能一样,不仅是一种思考和设计程序的方式。
mipadi

2
@mipadi-因此您可以在可用工具允许的范围内以任何语言应用功能原理。您可以使用Python(在一定范围内)编写功能样式的代码,在这种程度上,Python是一种功能语言。您可以用ML编写命令式代码,在这种情况下ML是命令式语言。这与“不良程序员可以用任何语言编写Fortran”并不完全相同,尽管很明显,如果您误解我的观点,这很容易陷入陷阱。
Steve314 2011年

1
@mipadi-但是如果命令式语言具有所有常规的函数构造,则可以使用函数式语言进行任何操作-差距将被消除,问题将是“什么是最好的方法”而不是“什么是功能/命令方式”。ML系列确实已经消除了这个鸿沟,但是由于它被称为功能性,我们将其样式视为功能性样式,而不是简单的良好样式。
Steve314 2011年

Answers:


24

在哪种情况下,我应该考虑一种更适合执行给定任务的功能编程语言?除了最近流行的并行编程多核问题。

任何涉及使用大量转换步骤创建派生数据元素序列的操作。

本质上是“电子表格问题”。您具有一些初始数据和一组逐行计算以应用于该数据。

我们的生产应用程序对数据进行大量统计汇总;从功能上来说,这是最好的方法。

我们要做的一件事是在三个巨大的数据集之间进行匹配合并。与SQL连接类似,但不通用。随后是许多派生数据的计算。这些全都是功能上的转换。

该应用程序是用Python编写的,但是使用生成器函数和不可变的命名元组以功能样式编写。它是低级功能的组成。

这是功能组合的具体示例。

for line in ( l.split(":") for l in ( l.strip() for l in someFile ) ):
    print line[0], line[3]

这是函数式编程影响Python等语言的一种方式。

有时这种事情写成:

cleaned = ( l.strip() for l in someFile )
split = ( l.split(":") for l in cleaned )
for line in split:
     print line[0], line[3]

如果我决定改用功能性编程语言,您认为我将面临的最大陷阱是什么?(除了范式更改和由于懒惰的评估而难以评估性能)。

不可变的对象是最困难的障碍。

通常,您会计算出创建新对象的值,而不是更新现有对象。它是对象的可变属性的想法很难打破。

派生的属性或方法函数是一种更好的方法。有状态的对象很难打破。

有这么多的函数式编程语言,您将如何选择一种更适合您的需求的语言?

起初没关系。选择任何语言进行学习。一旦知道了什么,您就可以考虑选择另一个以更好地满足您的需求。

我读过Haskell只是为了了解Python缺少的东西。


5
@edalorzo:Python不是弱类型的。它的类型非常非常强。没有强制转换运算符,因此它比Java或C ++具有更强的类型。
S.Lott

5
@ S.Lott-静态类型检查对IDE重构工具和intellisense类型的东西没有帮助吗?如果您正在进行后台编译,并且正在静态检查类型,这是否意味着您可以在工具中实现更多自动化?我确实同意,如果您的测试中有100%的代码覆盖率,它将抓住它。您必须意识到,实际上企业中可能只有不到50%的生产代码具有单元测试覆盖率,对吗?:)有一个真实的世界在那里我们住进去
斯科特惠特洛克

8
@ S.Lott“静态类型检查并不是全部有用。编译器进行静态检查或运行时检查都没关系。您仍然编写相同数量的代码和相同数量的单元测试。 ” 嗯不 静态类型检查的全部要点是可以捕获错误,因此,它可以大量减少所需的测试量。
乔恩·哈罗普

3
@ S.Lott“ Python的类型不是很弱。它的类型非常非常强。” Python隐式地在数字类型之间进行强制类型转换。
乔恩·哈罗普

3
@ Steve314“毕竟,静态类型检查会捕获一些符合某些基本规则的错误。从原则上讲,单元测试可以捕获所有这些错误,甚至更多。” 不能。静态检查证明了程序某个方面的正确性。单元测试试图证明正确性,但不能证明任何事物的正确性。单元测试不能替代静态类型检查或任何其他类型的证明。
乔恩·哈罗普

23

“功能”是一堆不同的功能,每个功能都是独立有用的,我发现单独查看每个功能会更有用。

不变性

现在,我已经熟悉了它,只要我能回去返回不变的结果,即使在面向对象的程序中,我也总是会尝试这样做。如果您具有值类型的数据,则更容易对程序进行推理。通常,您需要针对GUI和性能瓶颈之类的可变性。我的实体(使用NHibernate)也是可变的(这很有意义,因为它们正在对存储在数据库中的数据进行建模)。

用作头等舱类型

无论您想调用它什么,传递委托,动作或函数都是解决诸如“中间模式漏洞”之类的一整套现实世界问题的非常方便的方法。我还发现,将委托,操作或函数传递给对象比使该类声明一个事件并挂接该事件(假设通常只有一个“侦听器”)更干净。当您知道有一个侦听器时,则可以将回调操作作为构造函数参数传递(并存储在不可变成员中!)

能够组合功能(例如,Action<T>仅转换为an Action在某些情况下也非常有用)。

我们还要在这里注意Lambda语法,因为只有在将函数提升为第一类类型时才获得Lambda语法。Lambda语法可以非常富有表现力和简洁。

单音

诚然,这是我的弱点,但我的理解是F#中的计算工作流(例如async工作流)是monad。这是一个微妙但非常强大的构造。它与yield用于IEnumerable在C#中创建类的关键字一样强大。本质上,它是在幕后为您构建状态机,但是您的逻辑看起来很线性。

延迟评估和递归

我将它们放在一起是因为尽管它们总是作为函数式编程的特性而被人们所接受,但是它们已经如此迅速地进入了势在必行的语言中,以至于很难再将它们称为函数了。

S表达式

我想我不确定将其放在哪里,但是在某种程度上,将未编译的代码作为对象(并检查/修改)的能力,例如Lisp S-Expressions或LINQ Expressions,函数式编程的最强大工具。大多数新的.NET“流利”接口和DSL都使用lambda语法和LINQ表达式的组合来创建一些非常简洁的API。更不用说Linq2Sql / Linq2Nhibernate,其中您的C#代码被“神奇地”执行为SQL而不是C#代码。

那是对问题的第一部分的长答案...现在...

如果我决定改用功能性编程语言,您认为我将面临的最大陷阱是什么?(除了范式更改和由于懒惰的评估而难以评估性能)。

我面临的最大陷阱是试图找到使用功能解决方案与命令性解决方案之间的界线。但是,在尝试了两种方法之后,您会开始感觉会更好。

有这么多的函数式编程语言,您将如何选择一种更适合您的需求的语言?

如果您熟悉.NET,我强烈建议您使用F#。另一方面,如果您对JVM更加熟悉,那么总会有Clojure。如果您比实际更学术,那么我会选择Common Lisp或Scheme。如果您已经了解Python,我相信那里已经有很多功能构造。


@Scott感谢您的详细解释。我想您所说的最大陷阱将通过使用像Haskell这样的纯函数式编程语言来消除。这就是为什么我开始使用它的原因,因为我一生都是当务之急的程序员,而且我不想因不纯的编程语言而无所适从,并且冒着不学习好方法的风险。如果您不介意我问另一个问题:令我担心的两件事是工具和库。F#与其他.Net工具和库的集成有多好?
edalorzo 2011年

我不明白为什么您会在运行时合并代码,并在运行时代码生成和功能编程中进行检查。两者之间没有关系。您描述的元编程功能可以添加到几乎任何语言中,无论是否具有功能。我认为lisp开始将代码作为数据趋势发展,但是现在可以在ruby和其他非功能性语言中使用。
davidk01 2011年

1
@edalorzo-F#与.NET的集成非常好,只有一个小例外:没有GUI设计器。通过在C#程序集中设计GUI并从F#中引用它,或仅在代码中(而不是与设计人员一起)在F#中生成GUI即可解决此问题。
斯科特·惠特洛克

@ davidk01-有关元编程的好问题。我只是发现lambda语法和元编程是相互补充的,但是您是对的,它们可以分开。似乎您更有可能使用功能性语言进行元编程。
Scott Whitlock

1
@Scott:我认为它在许多方面都比较容易-例如,当您不必担心副作用时,可以更加自信地即时修改一段代码-它限制了您必须更改的内容。
Michael K

15

这已经有相当长的一段时间了。函数式编程根本没有像其他模型那样流行(即面向对象的编程)。

如果您算上由专业程序员(或至少有人认为自己是这样的人)开发的程序,这是正确的。如果您将网络扩展到包括不考虑自己的人开发的程序,则FP(或至少以功能风格进行编程)与OO(Excel,Mathematica,Matlab,R ...甚至是“现代” JavaScript)非常接近。 )。

在哪种情况下,我应该考虑一种更适合执行给定任务的功能编程语言?除了最近流行的并行编程多核问题。

我个人认为多核并不是FP的杀手feature(至少在Haskell,Scala,Clojure,F#编译器解决了缓存局部性问题之前)。杀手特征是mapfilterfold和朋友其允许一大组的算法的更简洁表达。FP语言的语法比大多数流行的OO同类语言更为简洁。

此外,FP更接近于关系模型,从而减少了与RDBMS的阻抗失配……这至少对于非程序员来说还是非常好。

同样,当您特别难以满足“正确性”要求时,以难以测试的形式(在科学计算/大数据分析中很常见,其目的是获得以前未知的结果,因此无法指定结果)FP可以提供好处。

如果我决定改用功能性编程语言,您认为我将面临的最大陷阱是什么?

  • 缺乏工具支持(F#和一些Lisps是例外,Scala即将出现)
  • 很难从硬件中挤出最后的性能
  • 社区通常专注于与大量商业软件开发项目所面临的问题不同的问题
  • 很少有在工业环境中使用FP的经验丰富的开发人员,如果您找到他们,您可能必须与金融业提供的薪水和福利竞争。
  • 编程的功能风格往往更难调试;即,在大多数(所有?)调试器中,通常不可能观察到较长的组合函数结果之间的差异

有这么多的函数式编程语言,您将如何选择一种更适合您的需求的语言?

  • 通过退出现有平台上的库/框架(例如JVM或.Net),可以解决多少个问题?是否有语言结构能够直接表达这些问题?

  • 您需要对应用程序的空间和时间性能进行多少底层控制?

  • 您的“正确性”要求有多严格?

  • 您能负担得起再培训开发人员和/或与软件开发中一些高利润的利基市场提供的利益竞争的能力吗?


+1 @Alexander这无疑是此讨论中最好的答案之一。通过引入人为因素,即寻找熟练的开发人员并使他们保持积极性的困难,您为讨论带来了新的角度。我完全同意您对FP语言的杀手级功能的看法,因为每天我也看到越来越多的命令式语言也采用了它们。但是您的帖子让我大开眼界,意识到关于FP我还有很多未知的事情。最后,基于.Net或Java之类的现有平台的观点当然是非常有效且完全明智的。
edalorzo 2011年

9

如果我决定改用功能性编程语言,您认为我将面临的最大陷阱是什么?(除了范式更改和由于懒惰的评估而难以评估性能)。

假设您是行业中的C ++ / C#/ Java开发人员...

为不想学习任何东西的脾气暴躁的同伴做好准备。为强硬的上司施加不好的语言选择做好准备,“因为他们曾经是编码员”。在论坛上为卑鄙的学者做好准备,以鼓励您了解类人猿。为无休止的语言战争做好准备,因为Scala甚至都没有消除尾声,而Clojure确实需要所有支架都装有脚踏板,也不要让我开始使用Erlang。

如果您是网络程序员,那么最大的陷阱可能就是您的头发。

有这么多的函数式编程语言,您将如何选择一种更适合您的需求的语言?

我将从平台开始:

  • OCaml在Linux上很棒,而在Windows上却很糟糕。
  • F#在Windows上很棒,在Linux上很棒。
  • Scala和Clojure在JVM上很棒。

1
当我读到“为脾气暴躁的朋辈准备,他们不想学习任何东西。” 我想大喊:“是如此真实!如此该死的真实!” 真的很伤心
Zelphir Kaltstahl

3

对于此问题的一个(公认的偏见)观点,您可以查看Bob Harper的博客Existential Type。卡内基·梅隆(Carnegie Mellon)最近重新设计了他们的CS课程,首先教功能编程,只有建立了牢固的功能编程基础之后,其他范式才被教,随着新课程的实际实施,哈珀(Harper)受到了打击。

哈珀(Harper)是标准ML编程语言的主要开发者之一,因此可以公平地说他对此事的看法是可以事先猜测的,并且他当然不反对有争议的陈述来争论这一立场,但是他做到了他的情况很好。


1
+1这篇文章当然很有趣,从现在开始,我会将其保留在我的最爱中。我认为,首先教FP无疑是一种不错的方法,因为如果不这样做,很难摆脱命令式思考,最重要的是,如果您不使用纯函数式编程语言,则很难打破传统。习惯。我还看到,主要的OOP语言都包含了功能特性,因此在不久的将来,获得功能性程序员的技能和心态必须非常方便。有趣的见解,谢谢!
edalorzo 2011年

@jimwise:为Harper的文章+1。我觉得他的方法很合适。@edalorzo:当您开始进行功能性思考时,您会发现命令式范式是在程序不够快时必须应用以优化程序的最后手段。我最近在Scala中编写了一些小型工具,虽然不是很大,但是却很琐碎,并且具有一些实际功能。令我惊讶的是,我一次都没有使用过var任何可变的收藏。因此,IMO的当务之急是某种过早的优化,众所周知,这是万恶之源。
乔治

2

在哪种情况下,我应该考虑一种更适合执行给定任务的功能编程语言?除了最近流行的并行编程多核问题。

没有任何神奇的公式可以告诉您何时使用函数式编程。并不是说面向对象编程更适合我们当前的编程情况。这只是用另一组抽象来构造程序的另一种方式。

如果我决定改用功能性编程语言,您认为我将面临的最大陷阱是什么?(除了范式更改和由于懒惰的评估而难以评估性能)。

函数式编程与懒惰无关。ML和OCaml是实用且严格的语言。您将面临的最大障碍是,用不变的值来构造事物,并且将您的头放在类型系统中用于副作用的任何抽象上。功能语言更适合优化,因为它们使类型系统中的副作用非常明显。Haskell使用monad,但还有其他方法可以在纯函数语言中使用效果。清洁具有唯一性类型,而开发中的某些其他语言具有其他功能。

有这么多的函数式编程语言,您将如何选择一种更适合您的需求的语言?

在我所知道的编程语言中,我只能说Haskell和Clean可以称为功能语言。其他所有方法都允许副作用,而无需在类型系统中明确显示这些效果。因此,如果您要花时间学习函数式编程,那么Haskell可能是唯一适合该法案的人。我所知道的所有其他语言(Erlang,Scala,Clojure等)仅在命令式语言之上提供了功能抽象。因此,如果您想按位处理功能范式,那么我建议使用Scala或Erlang,如果您想立即解决所有问题并可能沮丧地放弃,则应该选择Haskell。


@ Dadivk01 +1我喜欢FP lang就像其他任何竞争工具一样可以被用来解决与其他模型所解决的相同问题的推理。那么,为什么您认为它们不那么受欢迎呢?并且,您是否仍然同意它们比大多数OOP langs更适合解决并行计算问题?您是否会说与命令式lang相比,不可变的数据类型对内存占用量和性能有任何影响?我给了Haskell一个机会,为什么你会说“沮丧地放弃”,你在吓
right

@edalorzo:我认为函数式编程对于并行算法没有什么更好的。人们将声明式编程与函数式编程混为一谈。函数式编程的目的是声明性,一群聪明的人在编写出色的编译器以将声明性代码转换为机器代码。任何其他更倾向于描述问题而不是明确指出次要细节的语言对于并行编程也同样适用。
davidk01 2011年

@edalorzo:至于“无奈地放弃”,我之所以这样说是因为,如果您只知道命令式编程,那么Haskell的类型系统及其描述效果的单子方法可能有点过多。我认为最好从一种对Erlang和Scala这样的副作用采取更加务实的方法开始的语言开始。
davidk01 2011年

0

我将当前功能语言中的兴趣上升归因于它们是并行计算的理想选择。例如,map-reduce的整个思想都基于功能范式。毫无疑问,并行计算将会兴起,因为很明显,与横向扩展相比,当前横向扩展更容易,更便宜。即使在消费市场上,CPU也会获得更多核心,而不是更多GHz。

编辑:因为不是所有人都知道。

在纯函数式编程中,函数需要输入,产生输出且没有副作用。没有副作用意味着没有共享状态,因此不需要同步机制,否则在并行运行时将需要同步机制。同步是任何并发/并行软件中最困难的部分,因此,它纯粹是功能性的,您根本不需要处理最困难的部分。

至于map-reduce,甚至名称都来自函数编程(映射和归约都是函数范式中的典型操作)。map-reduce的两个步骤都是函数,这些函数并行运行,获取输入,产生输出并且没有副作用。因此,这恰好是纯函数式编程思想。

用于并行性的FP的几个示例:

  • CouchDB-在Erlang上构建
  • Facebook聊天-基于Erlang
  • Twitter-大部分基于Scala

3
每个人都对并行编程提出了要求,但是很少有数据可以备份它。如果您知道任何此类来源,则应引用它们。复杂的计算通常具有复杂的数据依赖性,如果使用功能编程范例,某些模型的固有数据相互依赖性不会神奇地消失。GPU编程是平行的,因为它得到,但他们通过使用命令式语言就好了放手,因为他们已经具有内置的并行工作的模式。
davidk01

4
@vartec:为了客观起见,如果您可以提供支持您的主张的参考,那还是不错的。这将对无知的读者提供更多帮助,而不仅仅是反问...
blubb 2011年

1
@vartec:其实不,我从没想到有很多人声称这是真的。我指责我接受过数学培训,但嘿,并非所有人都相信诸如事实和为我们的主张提供具体理由之类的内容,而您的编辑仍然没有解决我的意见。
davidk01 2011年

1
@vartec您可以引用支持您主张的可靠来源。
quant_dev

1
+1 @ davidk01“每个人都对并行编程提出了要求,但是几乎没有数据可以备份它”。我知道有大量相反的证据。例如,Cilk论文描述了如果您想要良好的缓存复杂性(就多核上可伸缩并行性的要求),就地突变是多么重要。
乔恩·哈罗普
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.