功能语言能否很好地应对复杂性?

时间:2010-02-02 20:42:31

标签: functional-programming complexity-theory

我很好奇函数式语言(一般来说)与大型程序的C#和Java等“传统”语言的比较。与使用非功能性语言相比,程序流程是否难以更快地遵循?使用函数式语言编写大型软件项目时是否还有其他问题需要考虑?

谢谢!

6 个答案:

答案 0 :(得分:14)

功能编程旨在通过将每个操作与其他操作隔离来减少大型系统的复杂性。当你编程没有副作用时,你知道你可以单独看每个函数 - 是的,理解一个函数也可能涉及理解其他函数,但至少你知道它不会干扰其他一些系统状态别处。

当然,这是假设完全纯粹的函数式编程 - 当然并非总是如此。您也可以以功能性方式使用更多传统语言,尽可能避免副作用。但原则很重要:避免副作用导致更易于维护,可理解和可测试的代码。

答案 1 :(得分:9)

  

与使用>非功能性语言相比,程序流程难以更快地遵循吗?

“程序流程”可能是分析大型功能程序的错误概念。控制流可以变成巴洛克式,因为有更高阶的函数,但这些通常很容易理解,因为很少有任何共享的可变状态需要担心,所以你可以只考虑参数和结果。当然,我的经验是,我发现遵循一个积极的功能程序比一个积极的面向对象程序更容易,其中部分实现被涂抹在许多类上。而且我发现使用高阶函数编写的程序比使用动态调度更容易。我还观察到,我的学生,作为一个整体,更能代表程序员,在继承和动态调度方面都存在困难。它们与高阶函数没有类似的困难。

  

编写大型文件时是否还有其他问题需要考虑   使用函数式语言的软件项目?

关键是一个好的模块系统。这是一些评论。

  • 我所知道的最强大的模块系统是由Matthew Flatt和Matthias Felleisen设计的unit system of PLT Scheme。遗憾的是,这个非常强大的系统缺少静态类型,我发现它对编程非常有帮助。

  • 下一个最强大的系统是标准ML模块系统。不幸的是,标准ML虽然很有表现力,但也允许很多可疑的结构,因此业余爱好者很容易弄得一团糟。此外,许多程序员发现很难有效地使用标准ML模块。

    Objective Caml模块系统非常相似,但有一些差异可以缓解标准ML的最严重过度。这些语言实际上非常相似,但Objective Caml的风格和习语使得初学者编写疯狂程序的可能性大大降低。

  • 功能语言最不强大/最富有表现力的模块系统是Haskell模块系统。这个系统有一个严重的缺陷,即没有明确的接口,因此失去了模块的大部分认知益处。另一个令人遗憾的结果是,虽然Haskell模块系统为用户提供了一个分层名称空间,但是通常不赞成使用此名称空间(import qualified,以防您是内部人员),并且许多Haskell程序员编写代码就像所有内容一样在一个大而扁平的命名空间中。这种做法相当于放弃了模块的另一大好处。

如果我必须用功能语言编写一个大系统并且必须确保其他人理解它,我可能会选择标准ML,并且我会为模块系统的使用建立非常严格的编程约定。 (例如,无处不在的显式签名,:>的opague归属,以及任何地方都不使用open。)对我来说,标准ML核心语言(与OCaml相比)的简单性和功能性更强标准ML Basis库的性质(与OCaml相比)比OCaml模块系统的优越性更有价值。

我只参与了一个非常大的Haskell程序,虽然我发现(并继续发现)在Haskell工作非常愉快,但我真的很想念没有明确的签名。

  

功能语言是否能很好地应对复杂性?

有些人这样做。我发现ML模块和模块类型(标准ML和Objective Caml)都是非常有用的工具,用于管理复杂性,理解复杂性以及在大型程序的不同部分之间放置不可破坏的防火墙。我对Haskell的经验不太好。


最后说明:这些并不是真正的新问题。将系统分解为具有由编译器检查的单独接口的模块在Ada,C,C ++,CLU,Modula-3中是一个问题,并且我确信许多其他语言。标准ML或Caml等系统的主要好处是可以获得明确的签名和模块化类型检查(C ++社区目前正在围绕模板和概念进行讨论)。我怀疑这些问题是永恒的,对任何大型系统都很重要,无论实施语言如何。

答案 2 :(得分:5)

我说的恰恰相反。由于缺乏副作用,更容易推理用函数式语言编写的程序。

答案 3 :(得分:0)

通常它不是“功能性”与“程序性”的问题;这是一个懒惰的评估问题。

懒惰评估是指您可以在不实际计算值的情况下处理值;相反,该值附加到一个表达式,如果需要,该表达式应该产生该值。具有惰性评估的语言的主要示例是Haskell。懒惰的评估允许定义和处理概念上无限的数据结构,所以这很酷,但它也使人类程序员更难以在他的脑海中密切关注将在他的计算机上真正发生的事情序列

由于大多数历史原因,大多数具有延迟评估的语言都是“功能性的”。我的意思是这些语言对通常具有功能的结构具有良好的语法支持。

在没有惰性评估的情况下,功能和过程语言允许表达相同的算法,具有相同的复杂性和类似的“可读性”。功能语言倾向于重视“纯函数”,即没有副作用的函数。纯函数的评估顺序是无关紧要的:从这个意义上说,纯函数可以帮助程序员通过简单地标记部件来了解发生了什么,因为知道什么顺序不重要。但这是间接的好处,纯函数也出现在过程语言中。

答案 4 :(得分:0)

据我所知,这是功能语言应对复杂性的关键优势:

  1. 功能编程讨厌副作用。 你可以真正黑盒子不同的层 你不会害怕并行处理 (Erlang中的actor model非常容易使用 而不是锁和线程。)
  2. 文化上,功能性程序员 用于设计DSL来表达 并解决问题。确定根本 问题的原语是根本性的 不同的方法比冲向品牌 新时尚框架。
  3. 从历史上看,这个领域一直由非常聪明的人领导: 垃圾收集,面向对象,元编程...... 所有这些概念首先在功能平台上实现。 有很多文献。
  4. 但这些语言的缺点是他们缺乏行业支持和经验。具有可移植性,性能和互操作性可能是一个真正的挑战,在像Java这样的其他平台上,所有这一切似乎都很明显。也就是说,基于像Scala这样的JVM语言可能非常适合双方都受益。

答案 5 :(得分:0)

  

程序流程是否变得困难   比如果更快地跟随   使用非功能语言?

可能就是这种情况,因为功能风格鼓励程序员更喜欢在抽象,逻辑转换,将输入映射到输出方面进行思考。在“程序流程”方面的思考假定了一种顺序的,有状态的操作模式 - 虽然功能程序可能具有“引擎盖下”的顺序状态,但它通常不是围绕这种状态构建的。

通过比较“处理数据集合”的命令式和功能性方法,可以很容易地看出透视的差异。前者倾向于使用结构化迭代,如forwhile循环,告诉程序“执行此任务序列,然后移动到下一个任务并重复,直到完成”。后者倾向于使用抽象递归,如foldmap函数,告诉程序“这是一个组合/转换元素的函数 - 现在使用它”。没有必要通过像map之类的函数来跟随递归程序流;因为它是一种无状态的抽象,所以从它意味着什么而不是它正在做的事情来思考就足够了。

或许有点说明功能方法已慢慢渗入非功能语言 - 考虑foreach循环,Python的列表推导......