reduce
(aka foldL
in FP) is the most general iterative higher order function in Javascript. You can implement, for instance, map
or
Lazy evaluation solves this trivially. While we don't have that in JavaScript, we can emulate it by passing a function instead of a value:
const foldR = f => acc => xs => xs.length
? f(xs[0])(() => foldR(f)(acc)(xs.slice(1)))
: acc // ^^^^^ "lazy"
const map = f => foldR(x => acc => [f(x)].concat(acc()))([])
const every = f => foldR(x => acc => f(x) && acc())(true)
// ^^^^^^^^ short-circuited - f(x) ? acc() : false
let xs = [1, 2, 3, 4];
console.log(map(x => x+1)(xs)); // [2, 3, 4, 5]
console.log(every(x => x%2==0)(xs)); // false
An alternative would be to use CPS, where you can easily jump to the end of the function:
const foldL = f => acc => xs => cont => xs.length
? f(acc)(xs[0])(res => foldL(f)(res)(xs.slice(1))(cont))
: cont(acc);
const map = f => foldL(acc => x => cont => f(x)(res => cont(acc.concat([res]))))([]);
//const every = f => // xs => cont =>
// foldL(acc => x => c => f(x)(res => c(acc && res)))(true) // (xs)(cont)
// ^^^^ eager call
const every = f => xs => cont =>
foldL(acc => x => c => acc ? f(x)(c) : cont(false))(true)(xs)(cont)
// call only when acc=true ^^^^ ^^^^^^^^^^^ jump out early otherwise
let xs = [1, 2, 3, 4];
let inc = x => cont => cont(x+1);
map(inc)(xs)(console.log.bind(console)); // [2, 3, 4, 5]
let even = x => cont => cont(x%2==0)
every(even)(xs)(console.log.bind(console)); // false