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.