函数式编程的陷阱/缺点

发布于 2024-08-12 14:39:56 字数 1432 浏览 6 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

江湖彼岸 2024-08-19 14:39:56

我很难想到函数式编程的许多缺点。再说一次,我是国际函数式编程会议的前主席,所以你可以放心地认为我有偏见。

我认为主要的缺点与隔离和进入壁垒有关。学习编写优秀函数式程序意味着学会以不同的方式思考,而要做好它需要投入大量的时间和精力。没有老师就很难学习。这些属性导致了一些缺点:

  • 由新手编写的函数式程序可能会不必要地慢 — 比 C 新手编写的 C 程序更有可能。另一方面,它大约是同样,新手编写的 C++ 程序也可能会变得不必要的缓慢。 (所有这些闪亮的功能......)

    一般来说,专家们编写快速函数式程序没有困难;事实上,一些在 8 核和 16 核处理器上性能最佳的并行程序现在都是用 Haskell 编写的。< /p>

  • 与开始使用 Python 或 Visual Basic 等语言的人相比,开始函数式编程的人在实现承诺的生产力提升之前更有可能放弃。只是没有那么多书籍和开发工具形式的支持。

  • 可以交谈的人越来越少了。 Stackoverflow 就是一个很好的例子;相对较少的 Haskell 程序员定期访问该网站(尽管部分原因是 Haskell 程序员有自己活跃的论坛,该论坛比 Stackoverflow 更古老、更完善)。

    确实,你不能很容易地与邻居交谈,因为函数式编程概念比 Smalltalk、Ruby 和 C++ 等语言背后的面向对象概念更难教、更难学。而且,面向对象社区花了数年时间为他们所做的事情开发出很好的解释,而函数式编程社区似乎认为他们的东西显然很棒,不需要任何特殊的隐喻或词汇来解释。 (他们错了。我还在等待第一本伟大的书函数式设计模式。)

  • 惰性函数式编程的众所周知的缺点(适用于 Haskell 或干净,但不是 ML、Scheme 或 Clojure)的原因是很难预测评估惰性函数程序的时间和空间成本——即使是专家也做不到它。这个问题是范式的基础,并且不会消失。有一些出色的工具可以用于发现时间和空间行为事后,但要有效地使用它们,您必须已经成为专家。

It's hard for me to think of many downsides to functional programming. Then again, I am a former chair of the International Conference on Functional Programming, so you may safely assume I am biased.

I think the main downsides have to do with isolation and with barriers to entry. Learning to write good functional programs means learning to think differently, and to do it well requires a substantial investment of time and effort. It is difficult to learn without a teacher. These properties lead to some downsides:

  • It is likely that a functional program written by a newcomer will be unnecessarily slow—more likely than, say, a C program written by a newcomer to C. On the other hand, it is about equally likely that a C++ program written by a newcomer will be unnecessarily slow. (All those shiny features...)

    Generally experts have no difficulty writing fast functional programs; and in fact some of the best-performing parallel programs on 8- and 16-core processors are now written in Haskell.

  • It's more likely that someone starting functional programming will give up before realizing the promised productivity gains than will someone starting, say, Python or Visual Basic. There just isn't as much support in the form of books and development tools.

  • There are fewer people to talk to. Stackoverflow is a good example; relatively few Haskell programmers visit the site regularly (although part of this is that Haskell programmers have their own lively forums which are much older and better established than Stackoverflow).

    It's also true that you can't talk to your neighbor very easily, because functional-programming concepts are harder to teach and harder to learn than the object-oriented concepts behind languages like Smalltalk, Ruby, and C++. And also, the object-oriented community has spent years developing good explanations for what they do, whereas the functional-programming community seem to think that their stuff is obviously great and doesn't require any special metaphors or vocabulary for explanation. (They are wrong. I am still waiting for the first great book Functional Design Patterns.)

  • A well-known downside of lazy functional programming (applies to Haskell or Clean but not to ML or Scheme or Clojure) is that it is very difficult to predict the time and space costs of evaluating a lazy functional program—even experts can't do it. This problem is fundamental to the paradigm and is not going away. There are excellent tools for discovering time and space behavior post facto, but to use them effectively you have to be expert already.

话少情深 2024-08-19 14:39:56

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

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

(感谢 Jared Updike 提供差异列表建议。)

One big disadvantage to functional programming is that on a theoretical level, it doesn't match the hardware as well as most imperative languages. (This is the flip side of one of its obvious strengths, being able to express what you want done rather than how you want the computer to do it.)

For example, functional programming makes heavy use of recursion. This is fine in pure lambda calculus because mathematics' "stack" is unlimited. Of course, on real hardware, the stack is very much finite. Naively recursing over a large dataset can make your program go boom. Most functional languages optimize tail recursion so that this doesn't happen, but making an algorithm tail recursive can force you to do some rather unbeautiful code gymnastics (e.g., a tail-recursive map function creates a backwards list or has to build up a difference list, so it has to do extra work to get back to a normal mapped list in the correct order compared to the non-tail-recursive version).

(Thanks to Jared Updike for the difference list suggestion.)

靑春怀旧 2024-08-19 14:39:56

如果您的语言没有提供良好的机制来通过程序检测状态/异常行为(例如,用于单子绑定的语法糖),那么任何涉及状态/异常的任务都会变得繁琐。 (即使有了这些糖,有些人可能会发现在 FP 中处理状态/异常更困难。)

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

If your language does not provide good mechanisms to plumb state/exception behavior through your program (e.g. syntax sugars for monadic binds) then any task involving state/exceptions becomes a chore. (Even with these sugars, some people might find it harder to deal with state/exceptions in FP.)

Functional idioms often do lots of inversion-of-control or laziness, which often has a negative impact on debugging (using a debugger). (This is somewhat offset by FP being much less error-prone due to immutability/referential transparency, which means you'll need to debug less often.)

梦纸 2024-08-19 14:39:56

Philip Wadler 就此写了一篇论文(名为“为什么没有人使用函数式编程语言”),并解决了阻止人们使用函数式编程语言的实际陷阱:

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

Philip Wadler wrote a paper about this (called Why No One Uses Functional Programming Languages) and addressed the practical pitfalls stopping people from using FP languages:

Update: inaccessible old link for those with ACM access:

傲性难收 2024-08-19 14:39:56

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

用 SMLnj 编写。另外,请原谅这个有点做作的示例。)

datatype Animal = Dog | Cat;

fun happyNoise(Dog) = "pant pant"
  | happyNoise(Cat) = "purrrr";

fun excitedNoise(Dog) = "bark!"
  | excitedNoise(Cat) = "meow!";

我可以非常快速地添加以下内容:

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!";

考虑一下 不过,对于面向对象语言来说,情况恰恰相反。向抽象类添加新的子类非常容易,但如果您想向抽象类/接口添加新的抽象方法以供所有子类实现,则可能会很乏味。

Aside from speed or adoption issues and addressing a more basic issue, I've heard it put that with functional programming, it's very easy to add new functions for existing datatypes, but it's "hard" to add new datatypes. Consider:

(Written in SMLnj. Also, please excuse the somewhat contrived example.)

datatype Animal = Dog | Cat;

fun happyNoise(Dog) = "pant pant"
  | happyNoise(Cat) = "purrrr";

fun excitedNoise(Dog) = "bark!"
  | excitedNoise(Cat) = "meow!";

I can very quickly add the following:

fun angryNoise(Dog) = "grrrrrr"
  | angryNoise(Cat) = "hisssss";

However, if I add a new type to Animal, I have to go through each function to add support for it:

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!";

Notice, though, that the exact opposite is true for object-oriented languages. It's very easy to add a new subclass to an abstract class, but it can be tedious if you want to add a new abstract method to the abstract class/interface for all subclasses to implement.

若沐 2024-08-19 14:39:56

我只是想讲一个轶事,因为在我们说话的时候我正在学习 Haskell。我正在学习 Haskell,因为将函数与操作分离的想法吸引了我,并且由于纯函数与非纯函数的隔离,隐式并行化背后有一些非常性感的理论。

我已经学习函数的折叠类三天了。 Fold 似乎有一个非常简单的应用程序:获取一个列表并将其减少为单个值。 Haskell 为此实现了一个 foldlfoldr 。这两个函数的实现有很大不同。 foldl 有一个替代实现,称为 foldl'。除此之外,还有一个语法略有不同的版本,称为 foldr1foldl1 ,具有不同的初始值。其中有一个针对foldl1foldl1'的相应实现。好像所有这些都不是令人兴奋的, fold[lr].* 需要作为参数并在归约中内部使用的函数有两个单独的签名,只有一个变体适用于无限列表(r ),并且只有其中一个在常量内存中执行(据我所知(L),因为只有它需要一个redex)。要理解为什么foldr可以在无限列表上工作,至少需要对语言的惰性行为有一个很好的理解,并且需要了解一些小细节,即并非所有函数都会强制计算第二个参数。对于那些在大学里从未见过这些函数的人来说,这些函数的在线图表非常令人困惑。没有等效的 perldoc 。我找不到 Haskell prelude 中任何函数的功能的单一描述。 Prelude 是一种随核心一起预装的发行版。我最好的资源实际上是一个我从未见过的人(凯尔),他花费了大量的时间来帮助我。

哦,fold 不必将列表缩减为非列表类型标量,列表的恒等函数可以写成 foldr (:) [] [1,2,3,4] (您可以将要点累积到列表中)。

/我回去读书。

I just wanted to buzz in with an anecdote because I'm learning Haskell right now as we speak. I'm learning Haskell because the idea of separating functions from actions appeals to me and there are some really sexy theories behind implicit parallelization because of the isolation of pure functions from non-pure functions.

I've been learning the fold class of functions now for three days. Fold seems to have a very simple application: taking a list and reducing it to a single value. Haskell implements a foldl, and foldr for this. The two functions have massively different implementations. There is an alternate implementation of foldl, called foldl'. On top of this there is version with a slightly different syntax called foldr1 and foldl1 with different initial values. Of which there is a correspond implementation of foldl1' for foldl1. As if all of this wasn't mind blowing, the functions that fold[lr].* require as arguments and use internally in the reduction have two separate signatures, only one variant works on infinite lists (r), and only one of them is executed in constant memory (as I understand (L) because only it requires a redex). Understanding why foldr can work on infinite lists requires at least a decent understanding of the languages lazy-behavoir and the minor detail that not all functions will force the evaluation of second argument. The graphs online for these functions are confusing as hell for someone who never saw them in college. There is no perldoc equivalent. I can't find a single description of what any of the functions in the Haskell prelude do. The prelude is a kinda of a distribution preloaded that comes with core. My best resource is really a guy I've never met (Cale) who is helping me at a huge expense to his own time.

Oh, and fold doesn't have to reduce the list to a non-list type scalar, the identity function for lists can be written foldr (:) [] [1,2,3,4] (highlights that you can accumulate to a list).

/me goes back to reading.

深白境迁sunset 2024-08-19 14:39:56

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

  1. 大多数人发现函数式编程很难理解。这意味着您可能会更难编写功能代码,而且几乎可以肯定其他人也更难掌握它。
  2. 函数式编程语言通常比 c 等语言慢。随着时间的推移,这不再是一个问题(因为计算机变得越来越快,编译器变得越来越智能)。
  3. 由于不像命令式对应物那样广泛传播,因此很难找到常见编程问题的库和示例。 (例如,找到 Python 的东西几乎总是比 Haskell 更容易)
  4. 缺乏工具,特别是调试工具。它绝对不像打开 C# 的 Visual Studio 或 Java 的 eclipse 那么容易。

Here are some problems I've run into:

  1. Most people find functional programming to be difficult to understand. This means it will probably be harder for you to write functional code, and it will almost certainly be harder for someone else to pick it up.
  2. Functional programming languages are usually slower than a language like c would be. This is becoming less of an issue over time (because computers are getting faster, and compilers are getting smarter)
  3. Not being as wide spread as their imperative counterparts, it can be difficult to find libraries and examples for common programming problems. (For example its almost always easier to find something for Python, then it is for Haskell)
  4. There's a lack of tools, particularly for debugging. Its definitely not as easy as opening up Visual Studio for C#, or eclipse for Java.
記憶穿過時間隧道 2024-08-19 14:39:56

抛开函数式编程具体实现的细节,我看到了两个关键问题:

  1. 相对于命令式模型,选择一些现实世界问题的函数式模型更为实用,这似乎相对较少。当问题域是命令式的时,使用具有该特征的语言是自然而合理的选择(因为通常建议最小化规范和实现之间的距离,作为减少细微错误数量的一部分)。是的,这可以通过足够聪明的编码器来克服,但是如果您需要 Rock Star 编码器来完成任务,那是因为它太难了。

  2. 出于某种我从未真正理解的原因,函数式编程语言(或者可能是它们的实现或社区?)更有可能希望拥有其语言中的所有内容。用其他语言编写的库的使用要少得多。如果其他人对某些复杂操作有特别好的实现,那么使用它而不是自己制作更有意义。我怀疑这在一定程度上是由于使用复杂的运行时而导致的,这使得处理外部代码(尤其是高效地执行)变得相当困难。我很乐意在这一点上被证明是错误的。

我想这都是由于编程研究人员比普通程序员更频繁地使用函数式编程而导致的实用主义普遍缺乏。一个好的工具可以让专家做出伟大的事情,但一个伟大的工具是让普通人能够完成专家通常可以做的事情,因为这是迄今为止更困难的任务。

Looking away from the details of specific implementations of functional programming, I see two key issues:

  1. It seems comparatively rare that it is practical to choose a functional model of some real-world problem over an imperative one. When the problem domain is imperative, using a language with that characteristic is a natural and reasonable choice (since it is in general advisable to minimize the distance between the specification and implementation as part of reducing the number of subtle bugs). Yes, this can be overcome by a smart-enough coder, but if you need Rock Star Coders for the task, it's because it's too bloody hard.

  2. For some reason that I never really understood, functional programming languages (or perhaps their implementations or communities?) are much more likely to want to have everything in their language. There's much less use of libraries written in other languages. If someone else has a particularly good implementation of some complex operation, it makes much more sense to use that instead of making your own. I suspect that this is in part a consequence of the use of complex runtimes which make handling foreign code (and especially doing it efficiently) rather difficult. I'd love to be proved wrong on this point.

I suppose these both come back to a general lack of pragmatism caused by functional programming being much more strongly used by programming researchers than common coders. A good tool can enable an expert to great things, but a great tool is one that enables the common man to approach what an expert can do normally, because that's by far the more difficult task.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文