reduce (aka foldL in FP) is the most general iterative higher order function in Javascript. You can implement, for instance, map or filter in terms of reduce. I've used an imperative loop to better illustrate the algorithm:
const foldL = f => acc => xs => {
for (let i = 0; i < xs.length; i++) {
acc = f(acc)(xs[i]);
}
return acc;
};
const map = f => xs => {
return foldL(acc => x => acc.concat([f(x)]))([])(xs);
}
let xs = [1, 2, 3, 4];
const inc = x => ++x;
const result = map(inc)(xs);
console.log(result); // [2, 3, 4, 5]
But you can't derive some or every from reduce, because both are able to return early.
So how can a even more generalized partial reduce function look like? Until now I've come up with following naive implementation:
const foldLP = f => pred => acc => xs => {
for (let i = 0, r; i < xs.length; i++) {
r = pred(i, acc, xs[i]);
if (r === true) { // normal iteration
acc = f(acc)(xs[i]);
} else if (r === false) { // early exit
break;
} /* else { // skip iteration
continue;
} */
}
return acc;
};
const takeN = n => (idx, acc, x) => idx < n;
const append = xs => ys => xs.concat(ys);
let xs = [1,2,3,4,5];
const result = foldLP(append)(takeN(3))([])(xs);
console.log(result); // [1,2,3]
I can also implement map in terms of foldLP:
const always = x => y => x;
const map = f => xs => {
return foldLP(acc => x => acc.concat([f(x)]))(always(true))([])(xs);
}
map(inc)(xs); // [2,3,4,5,6]
The drawback is obvious: Whenever an early exit mechanism isn't needed, always is invoked unnecessarily. The transforming and early exiting function are composed statically by foldLP and can't be used independently. Is there a more efficient combinator, that enables a generalized Array.prototype.reduce?
If you look at the call stack, the return statement of the reducing function acc => x => acc.concat([f(x)]) would have to skip several stack-frames. This kind of stack manipulation makes me think of continuations. Maybe there is an efficient way to solve this problem in Continuation Passing Style along with an adapted call/cc function - or at least with a generator.