Этот вопрос меня очень зацепил. Вот некоторые из моих мыслей.
Такое чувство, что использование underscore.js в режиме 'chain ()' выходит за рамки парадигмы функционального программирования. По сути, вместо вызова функций для функций вы вызываете методы экземпляра объекта-оболочки способом ООП.
Я использую цепочку подчеркивания () сам здесь и там, но этот вопрос заставил меня задуматься. Что делать, если лучше просто создавать более значимые функции, которые затем можно вызывать в последовательности, без необходимости использовать chain (). Ваш пример будет выглядеть примерно так:
arr = [1,2,3]
double = (arr) -> _.map(arr, (el) -> 2 * el)
sum = (arr) -> _.reduce(arr, (s, n) -> s + n)
out = (r) -> 10 * r
result = out sum double arr
# probably a less ambiguous way to do it would be
result = out(sum(double arr))
Глядя на настоящие функциональные языки программирования (как в ... гораздо более функциональных, чем JavaScript), кажется, что вы могли бы сделать то же самое там еще более простым способом. Вот та же самая программа, написанная на Standard ML. Обратите внимание, что вызов map только с одним аргументом возвращает другую функцию. Нет необходимости заключать эту карту в другую функцию, как в JavaScript.
val arr = [1,2,3];
val double = map (fn x => 2*x);
val sum = foldl (fn (a,b) => a+b) 0;
val out = fn r => 10*r;
val result = out(sum(double arr))
Стандартный ML также позволяет создавать операторы, что означает, что мы можем сделать небольшой «цепной» оператор, который можно использовать для вызова этих функций в более интуитивном порядке.
infix 1 |>;
fun x |> f = f x;
val result = arr |> double |> sum |> out
Я также думаю, что в этой цепочке underscore.js есть что-то похожее на монады в функциональном программировании, но я мало что знаю о них. Тем не менее, я чувствую, что этот тип конвейера манипулирования данными - это не то, для чего вы обычно используете монады.
Я надеюсь, что кто-то с более функциональным опытом программирования сможет внести свой вклад и исправить меня, если я ошибаюсь по любому из пунктов выше.
UPDATE
Немного не по теме, но один из способов создания частичных функций может быть следующим:
// extend underscore with partialr function
_.mixin({
partialr: function (fn, context) {
var args = Array.prototype.slice.call(arguments, 2);
return function () {
return fn.apply(context, Array.prototype.slice.call(arguments).concat(args));
};
}
});
Теперь эту функцию можно использовать для создания частичной функции из любой функции подчеркивания, потому что большинство из них принимают входные данные в качестве первого аргумента. Например, функция суммы теперь может быть создана как
var sum = _.partialr(_.reduce, this, function (s, n) { return s + n; });
sum([1,2,3]);
Я все же предпочитаю arr |> double |> sum |> out вместо out (sum (double (arr)))). Цепочка Underscore () хороша тем, что читает в более естественном порядке.