As soon as we work with monads in Javascript we wind up with nested monadic computations, because there is no do-notation as in Haskell or multi-prompt coroutines as in Kotlin. We cannot use generators either, because they are insufficient for the job.
So are we stuck with nested monadic computations in an ad-hoc fashion like the following?
// Monad
const arrChain = mx => fm =>
arrFold(acc => x =>
arrAppend(acc) (fm(x))) ([]) (mx);
// auxiliary function
const arrFold = f => init => xs => {
let acc = init;
for (let i = 0; i < xs.length; i++)
acc = f(acc) (xs[i], i);
return acc;
};
const arrAppend = xs => ys =>
xs.concat(ys);
// MAIN
const main = arrChain([1,2]) (x => // nested monadic computation
arrChain([3,4]) (y =>
arrChain([5,6]) (z =>
x === 1
? []
: [x, y, z])));
main; // [2,3,5,2,3,6,2,4,5,2,4,6]
Actually, there is a small improvement. We can treat monadic actions like applicative functors by wrapping them in the context of their corresponding monad:
// Monad
const chain3 = chain => tx => ty => tz => fm =>
chain(chain(chain(tx) (x => fm(x)))
(gm => chain(ty) (y => gm(y))))
(hm => chain(tz) (z => hm(z)));
// MAIN
const main = chain3(arrChain) // much more readable
([1,2])
([3,4])
([5,6])
(x => x === 1
? []
: [y => [z => [x, y, z]]]);
main; // [2,3,5,2,3,6,2,4,5,2,4,6]
Given chain3
we can implement other arity aware combinators and overload them inside a variadic chainn
to gain more flexibility.
More on this and other FP topics in my course for FP in JS:
A fool's scriptum on functional programming.
Top comments (0)