Gor*_*son 68 paradigms functional-programming
你什么时候不想使用函数式编程?什么不太擅长?
我更多地寻找范式的缺点,而不是"没有广泛使用"或"没有好的调试器可用"之类的东西.到目前为止,这些答案可能是正确的,但它们处理FP是一个新概念(一个不可避免的问题)而不是任何固有的品质.
有关:
Nor*_*sey 46
我很难想到函数式编程的许多缺点.再说一次,我是国际功能编程大会的前任主席,所以你可以放心地认为我有偏见.
我认为主要缺点与隔离和进入障碍有关.学习编写好的功能性课程意味着学会以不同的方式思考,并且做得好,需要花费大量的时间和精力.没有老师就很难学.这些属性导致一些缺点:
新手编写的功能程序很可能会不必要地慢 - 比C新手编写的C程序更可能.另一方面,新手编写的C++程序也同样可能.会不必要地慢.(所有这些闪亮的功能......)
通常,专家编写快速功能程序没有困难; 实际上,8核和16核处理器上的一些性能最佳的并行程序现在都是用Haskell编写的.
开始函数式编程的人更有可能在实现承诺的生产力提升之前放弃,而不是像Python或Visual Basic那样开始.书籍和开发工具的形式并没有那么多的支持.
与人交谈的人数较少.Stackoverflow就是一个很好的例子; 相对较少的Haskell程序员定期访问该站点(尽管部分原因是Haskell程序员拥有自己的生动论坛,这些论坛比Stackoverflow更老,更成熟).
你也不能很容易地与你的邻居交谈,因为功能编程概念比Smalltalk,Ruby和C++等语言背后的面向对象概念更难教,更难学.而且,面向对象的社区花了数年时间为他们所做的事情开发了很好的解释,而功能编程社区似乎认为他们的东西显然是伟大的,并且不需要任何特殊的隐喻或词汇来解释.(他们错了.我还在等待第一本伟大的功能设计模式书.)
惰性函数式编程的一个众所周知的缺点(适用于Haskell或Clean但不适用于ML或Scheme或Clojure)是很难预测评估惰性函数程序的时间和空间成本 - 即使专家也做不到它.这个问题是范式的基础,并没有消失.事后有很好的工具来发现时间和空间行为,但要有效地使用它们,你必须已经是专家.
Jon*_*rop 30
我认为围绕函数式语言的废话是函数式编程的最大问题.当我开始在愤怒中使用函数式编程时,对我来说一个很大的障碍是理解为什么Lisp社区提出的许多高度进化的论证(例如关于宏和同音语法)是错误的.今天,我看到很多人在并行编程方面被Haskell社区欺骗了.
实际上,您不必再看这个线程就可以看到其中的一些:
"一般来说,专家编写快速功能程序并不困难;事实上,8核和16核处理器上的一些性能最佳的并行程序现在都是用Haskell编写的."
这样的陈述可能会给你一种专家选择Haskell的印象,因为它对于并行性来说非常好,但事实是Haskell的性能糟透,Haskell对多核并行性有好处的神话是由Haskell研究人员长期存在的,几乎没有关于并行性的知识谁只是在他们自己的集团控制下的期刊和会议的舒适区内发表,从而避免真正的同行评审.Haskell在真实世界的并行/多核/ HPC中是不可见的,因为它很难用于并行编程.
具体而言,多核编程中的真正挑战是利用CPU缓存来确保内核不会缺乏数据,这一问题在Haskell环境中从未得到解决.麻省理工学院的Charles Leiserson团队使用他们自己的Cilk语言很好地解释和解决了这个问题,Cilk语言继续成为英特尔TBB和微软TPL in .NET 4中多核实际并行编程的支柱.关于如何使用这种技术编写优雅的高级命令式代码的精湛描述,这些代码编译成2008年论文中可扩展的高性能代码多线程缓存遗忘算法的缓存复杂性.我在审查中解释了这一点 一些最先进的并行Haskell研究.
这给纯函数式编程范式留下了很大的疑问.这是你抽出时间和空间所付出的代价,这一直是这种陈述范式背后的主要动机.
编辑:德克萨斯多核技术公司最近也发现Haskell在多核并行的背景下没有给人留下深刻印象.
Chu*_*uck 29
函数式编程的一大缺点是在理论层面上,它与硬件以及大多数命令式语言都不匹配.(这是其明显的优势之一,能够表达的另一面是什么,你想要做的,而不是如何你想要的电脑做它.)
例如,函数式编程大量使用递归.这在纯lambda演算中很好,因为数学的"叠加"是无限的.当然,在真实硬件上,堆栈非常有限.天真地在大型数据集上递归可以使您的程序蓬勃发展.大多数函数式语言都优化了尾递归,因此不会发生这种情况,但是使算法尾递归会迫使你做一些相当不美观的代码体操(例如,尾递归映射函数创建一个向后列表或者必须建立差异list,所以与非尾递归版本相比,它必须做更多的工作才能以正确的顺序返回到普通的映射列表.
(感谢Jared Updike的差异列表建议.)
Bri*_*ian 23
如果您的语言没有提供通过您的程序检测状态/异常行为的良好机制(例如,monadic绑定的语法糖),那么任何涉及状态/异常的任务都会变成一件苦差事.(即使使用这些糖,有些人可能会发现在FP中处理状态/异常更加困难.)
功能习语通常会进行大量的控制反转或懒惰,这通常会对调试产生负面影响(使用调试器).(由于不变性/引用透明性,FP在某种程度上抵消了由于不变性/引用透明度而导致的错误更少的错误,这意味着您需要更少地进行调试.)
Jar*_*ike 13
菲利普·瓦德勒(Philip Wadler)写了一篇关于此的文章(称为"为什么没有人使用函数式编程语言"),并解决了阻止人们使用FP语言的实际陷阱:
更新:具有ACM访问权限的人无法访问旧链接:
除了速度或采用问题以及解决更基本的问题之外,我听说它通过函数式编程实现,为现有数据类型添加新函数非常容易,但添加新数据类型"很难".考虑:
(写在SMLnj.另外,请原谅有些人为的例子.)
datatype Animal = Dog | Cat;
fun happyNoise(Dog) = "pant pant"
| happyNoise(Cat) = "purrrr";
fun excitedNoise(Dog) = "bark!"
| excitedNoise(Cat) = "meow!";
Run Code Online (Sandbox Code Playgroud)
我可以很快添加以下内容:
fun angryNoise(Dog) = "grrrrrr"
| angryNoise(Cat) = "hisssss";
Run Code Online (Sandbox Code Playgroud)
但是,如果我向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!";
Run Code Online (Sandbox Code Playgroud)
但请注意,面向对象语言恰恰相反.向抽象类添加新的子类非常容易,但如果要将新的抽象方法添加到抽象类/接口以供所有子类实现,则可能会很繁琐.