Functional programming 函数式编程的陷阱/缺点

Functional programming 函数式编程的陷阱/缺点,functional-programming,paradigms,Functional Programming,Paradigms,什么时候您不想使用函数式编程?它不擅长什么 我更多的是寻找范式作为一个整体的缺点,而不是像“没有广泛使用”或“没有好的调试器可用”这样的东西。到目前为止,这些答案可能是正确的,但它们涉及到FP是一个新概念(一个不可避免的问题),而不是任何固有的特性 相关的: 如果您的语言没有提供良好的机制通过您的程序检查状态/异常行为(例如,一元绑定的语法糖),那么任何涉及状态/异常的任务都将成为一件麻烦事。(即使有了这些糖类,有些人可能会发现在FP中处理状态/异常会更加困难。) 函数式习惯用法通常会产生

什么时候您不想使用函数式编程?它不擅长什么

我更多的是寻找范式作为一个整体的缺点,而不是像“没有广泛使用”或“没有好的调试器可用”这样的东西。到目前为止,这些答案可能是正确的,但它们涉及到FP是一个新概念(一个不可避免的问题),而不是任何固有的特性

相关的:


如果您的语言没有提供良好的机制通过您的程序检查状态/异常行为(例如,一元绑定的语法糖),那么任何涉及状态/异常的任务都将成为一件麻烦事。(即使有了这些糖类,有些人可能会发现在FP中处理状态/异常会更加困难。)


函数式习惯用法通常会产生很多控制反转或惰性,这通常会对调试(使用调试器)产生负面影响。(这在一定程度上被FP的不易出错性所抵消,因为FP具有不变性/引用透明性,这意味着您需要更少的调试次数。)

以下是我遇到的一些问题:

  • 大多数人发现函数式编程很难理解。这意味着您可能更难编写功能代码,而其他人几乎肯定也更难掌握它
  • 函数式编程语言通常比c语言慢。随着时间的推移,这一问题越来越少(因为计算机越来越快,编译器越来越智能)
  • 由于没有它们的命令对应项广泛传播,因此很难找到常见编程问题的库和示例。(例如,为Python查找内容几乎总是比为Haskell查找更容易)
  • 缺少工具,特别是用于调试的工具。这绝对不像为C#打开VisualStudio或为Java打开eclipse那么容易

  • 除了速度或采用问题以及解决一个更基本的问题之外,我听说通过函数式编程,为现有数据类型添加新函数非常容易,但添加新数据类型“很难”。考虑:

    (用SMLnj书写。另外,请原谅这个有点做作的例子。)

    我可以很快添加以下内容:

    fun angryNoise(Dog) = "grrrrrr"
      | angryNoise(Cat) = "hisssss";
    
    但是,如果我向Animal添加了一个新类型,我必须通过每个函数来添加对它的支持:

    datatype Animal = Dog | Cat | Chicken;
    
    fun happyNoise(Dog) = "pant pant"
      | happyNoise(Cat) = "purrrr"
      | happyNoise(Chicken) = "cluck cluck";
    
    fun excitedNoise(Dog) = "bark!"
      | excitedNoise(Cat) = "meow!"
      | excitedNoise(Chicken) = "cock-a-doodle-doo!";
    
    fun angryNoise(Dog) = "grrrrrr"
      | angryNoise(Cat) = "hisssss"
      | angryNoise(Chicken) = "squaaaawk!";
    

    但是请注意,面向对象语言的情况正好相反。向抽象类中添加一个新的子类非常容易,但如果您想向抽象类/接口中添加一个新的抽象方法以供所有子类实现,则可能会非常繁琐。

    函数式编程的一个大缺点是,在理论层面上,它与硬件以及大多数命令式语言不匹配。(这是其明显优势之一的另一面,能够表达你想做什么,而不是你想让计算机如何做。)

    例如,函数式编程大量使用递归。这在纯lambda演算中很好,因为数学的“堆栈”是无限的。当然,在真正的硬件上,堆栈是非常有限的。在大数据集上天真地递归会使程序变得繁荣。大多数函数式语言都会优化尾部递归,这样就不会发生这种情况,但使算法尾部递归可能会迫使您执行一些相当不实用的代码练习(例如,尾部递归映射函数创建一个向后列表或必须建立一个差异列表,因此与非尾部递归版本相比,它必须做额外的工作才能以正确的顺序返回正常映射列表)


    (感谢贾里德·厄普代克(Jared Updike)提出的差异列表建议。)

    菲利普·瓦德勒(Philip Wadler)就此写了一篇论文(名为《为什么没有人使用函数式编程语言》),并指出了阻止人们使用FP语言的实际陷阱:

    更新:具有ACM访问权限的用户无法访问旧链接:


    我只想讲一个轶事,因为我现在正在学习Haskell。我学习Haskell是因为将函数与动作分离的想法吸引了我,而且隐式并行化背后有一些非常性感的理论,因为纯函数与非纯函数是分离的

    我已经学习了fold函数类三天了。fold似乎有一个非常简单的应用程序:获取一个列表并将其缩减为一个值。Haskell实现了一个
    foldl
    ,并为此实现了
    foldr
    。这两个函数的实现有很大不同。有一个
    fold的替代实现l
    ,称为
    foldl'
    。除此之外,还有一个版本的语法稍有不同,分别称为
    foldr1
    foldl1
    ,初始值不同。其中有一个
    foldl1'
    对应的
    foldl1
    实现。
    折叠的函数似乎所有这一切都不令人兴奋[lr].
    require作为参数并在归约中内部使用有两个独立的签名,只有一个变量在无限列表(r)上工作,并且只有一个变量在常量内存中执行(正如我所理解的(L),因为它只需要
    redex
    ).要理解为什么
    foldr
    可以在无限列表上工作,至少需要对语言lazy behavoir有一个良好的理解,以及不是所有函数都会强制计算第二个参数的小细节。这些函数的在线图表对于大学里从未见过它们的人来说非常混乱。没有
    perldoc
    等效。我找不到Haskell prelude中任何函数的单一描述。prelude是一种预装了core的发行版。我最好的资源是一个我从未见过的家伙(Cale),他在自己的时间里花费了巨大的代价帮助我

    哦,折叠不一定是r
    datatype Animal = Dog | Cat | Chicken;
    
    fun happyNoise(Dog) = "pant pant"
      | happyNoise(Cat) = "purrrr"
      | happyNoise(Chicken) = "cluck cluck";
    
    fun excitedNoise(Dog) = "bark!"
      | excitedNoise(Cat) = "meow!"
      | excitedNoise(Chicken) = "cock-a-doodle-doo!";
    
    fun angryNoise(Dog) = "grrrrrr"
      | angryNoise(Cat) = "hisssss"
      | angryNoise(Chicken) = "squaaaawk!";