3

我正在学习使用 Ramda 并且遇到了compose。但我无法理解它的目的

let value = R.compose( calledThird, calledSecond, calledFirst('hello') );

// vs

let value = calledThird( calledSecond( calledFirst('hello') ) );

纯粹是为了允许currying吗?也许...

let curried = R.compose( calledThird, calledSecond, calledFirst );

curried('hello');

有没有其他目的?

4

2 回答 2

14

您的第二个示例正是它的原因,尽管这实际上与柯里化无关。

功能组合允许您从更简单的功能中构建更复杂的功能。

想象一下,您有一些需要排序的数据类型,比如说约会的集合。鉴于你已经有一个sortByDate函数和一个reverse函数,你可以sortByDateDescending写成

var sortByDateDescending = function(appointments) {
    return reverse(sortByDate(appointments));
}

或者在 ES6 中:

const sortByDateDescending = appointments => reverse(sortByDate(appointments));

这并没有错。compose但是,如果您要使用助手编写它,它有几个优点:

var sortByDateDescending = compose(reverse, sortByDate);

首先,它显然更短,并且随着您添加更多功能,差异会变得更大,尤其是使用 es6 之前的代码。

但更重要的是,这使您可以专注于有意义的事情。您在这里组合功能;最终将通过的数据是目标,但是当您构建新功能时,它主要是分散注意力。

通过这种方式编写,您可以专注于正在发生的事情:您正在按日期对列表进行排序,然后您正在反转结果。

更专注于函数式编程的语言使这更加简单,使用不显眼的运算符而不是像compose. 在 Haskell 中,等价物看起来像

sortByDateDescending = reverse . sortByDate

但是 Javascript 并没有提供那种优雅。我们能做的最好的事情就是创建类似compose(或它的逆序双胞胎,pipe.)的函数

对 Ramda的介绍提供了更多以这种方式工作的示例。


顺便说一句,咖喱是完全不同的野兽。这是一种使在此类组合中重用函数变得更加容易的技术。但这主要是分散注意力。如果你有兴趣,我也有一个关于这个问题的帖子。

于 2016-01-19T14:08:27.263 回答
2

斯科特的回答很棒 - 只是想添加更多现实生活中的例子。

当你看到很多这种模式时,你可以找出改进代码的地方,在这些模式中,你不断地使用传递函数来处理一些数据:

var list = [3, 4, 1, 2];

list = filterOutEvens(list);
list = sort(list);
list = prependFoo(list);

你可能想做这样的事情:

[3, 4, 1, 2].filterOutEvens().sort().prependFoo();

但是你记得要让这成为可能,你必须向 Array.prototype 添加东西,这绝对是一个禁忌。

R.compose 为您提供了下一个最好的东西(请记住它从右到左工作):

var processList = R.compose(prependFoo, sort, filterOutEvens);
processList([3, 4, 1, 2]);

如果您想使用它,这是完整的示例:

function filterOutEvens(list) {
    return list.filter(function(item) {
        return item % 2 !== 0;
    });
}

function sort(list) {
    // More cleanly use R.sort here instead of native Array.prototype.sort, which operates in-place.
    var diff = function(a, b) { return a - b; };
    return R.sort(diff, list);
}

function prependFoo(list) {
    return list.map(function(item) {
        return 'foo-' + item;
    });
}

var processList = R.compose(prependFoo, sort, filterOutEvens);

var result = processList([3, 4, 1, 2]);
// -> ["foo-1", "foo-3"]

// Display Stackoverflow snippet output.
document.body.innerHTML = JSON.stringify(result, null, 2);
<script src="//cdn.jsdelivr.net/ramda/0.19.1/ramda.min.js"></script>

于 2016-02-27T05:43:36.720 回答