函数式编程中currying的价值是什么?

时间:2011-07-06 07:55:53

标签: functional-programming currying

我知道这个概念以及如何使用currying,但我想知道它在实践中的价值是什么?

3 个答案:

答案 0 :(得分:5)

正如相关问题所涵盖的那样,Practical use of curried functions?,人们重视和使用它的原因有很多,包括:

  • 改进代码重用 - 特殊情况函数只是部分应用(和curried)通用函数
  • 提高代码可读性 - map (+2)map (\x -> x + 2)
  • 更易于阅读
  • 改进性能 - currying可以明显某些特化,一个好的编译器会为你生成专门的版本
  • 有趣 - 更简单的代码,更漂亮的代码让生活更愉快。

答案 1 :(得分:2)

我发现了真正的好处:

  • 减少错误 - 按功能组合编写代码往往会产生比命令控制流更正确的代码。例如,如果您使用“map”而不是“for loops”,则可以消除许多“逐个”索引错误的风险

  • 更好的并发性 - 使用纯的,无副作用的函数创建的代码自动是线程安全的。将此与不可变的持久数据结构结合起来,您就可以编写强大的并发代码。 Clojure特别适合这一点 - 见http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey

  • 更简洁,更易于管理的代码 - 在我完全不科学的分析中,功能代码似乎要比命令式OOP代码短得多。随着计划变得更大,收益往往更加明显。我认为有几个原因:

    • 函数式语言的语法往往非常简洁。例如,Haskell和各种Lisps都有非常简单和优雅的语法
    • 功能语言倾向于鼓励组合(更高阶函数),而不是“粘合”程序的各个部分。因此,您可以避免许多其他范例中固有的样板。
    • 与构图点相关,我发现在函数式语言中应用DRY原则更容易。如果你看到一个常见的模式,几乎总是可以很容易地将它提取到一个更高阶的函数(或者如果你是一个Lisper那么宏),这样你就可以在其他地方使用它了。
  • 可测试性 - 当您主要使用纯函数编写代码时,编写健壮的测试非常容易。

当然有一些缺点:

  • 写起来更难 - 你需要更多的心理敏捷性,因为你需要掌握相当复杂的抽象概念。如果你是一个训练有素的数学家(我),它会有所帮助,但我仍然觉得编写功能代码比OOP更难。
  • 存在一些性能开销 - 函数式语言使用各种结构,这些结构必然意味着某种程度的开销。虽然这可以通过良好的编译器来实现,但它永远不会完全消除。
  • 图书馆/工具支持 - 这几乎完全是由于OOP平台和工具的成熟度更高,但它仍然是一个问题。从长远来看,这不会是一个问题,但我发现最好的解决方案是使用Clojure,它可以从大多数Java平台库和工具中受益。

答案 2 :(得分:1)

我会说它有点像Once and Only Once

在C / C ++中,我发现自己编写的代码如

configure_grid (grid, first_column, last_column, action) {
    for (i = first_column; i <= last_column; ++i)
        // ...
}

configure_grids (action) {
   congifure_grid (alpha, first_alpha, last_alpha, action);
   congifure_grid (beta, first_beta, last_beta, action);
}

而不是为每个alpha和beta写一次for-loop。这与程序代码中的讨论相似。这里的优势很明显。

Currying是一个重要的理论概念,但在实用术语中,这是优势。

事实上,我记得曾经在C中编写一个测试套件,它有点像这样:

typedef bool (*predicate) (const type *);

const char * argument;

bool do_foo (const type * t) {
    return bar (t, argument);
}

bool do_baz (const type * t) {
    return bap (t, argument);
}

predicate foo (const char * arg) {
    argument = arg;
    return do_foo;
}

predicate baz (const char * arg) {
    argument = arg;
    return do_baz;
}

assert (for_all (data_set("alpha"), foo ("abc")));
assert (for_all (data_set("beta"),  baz ("def")));

这完全是纯粹的C,没有宏观技巧等。功能风格和类似于currying。这里的优点是您可以准确地看到测试用例的确切内容。 data_set类似 - 它将其参数绑定到另一个获取数据的函数:for_all执行thunk,检查谓词,并清理。整齐。