Monadic computations quickly become confusing in JS:
const chain = fm => xs =>
xs.reduce((acc, x) => acc.concat(fm(x)), []);
const of = x => [x];
const main = xs => ys => zs =>
chain(x =>
x === 0
? []
: chain(y =>
chain(z => [[x, y, z]]) (zs)) (ys)) (xs);
console.log("run to completion",
main([1, 2]) (["a", "b"]) ([true, false]));
console.log("short circuiting",
main([0, 2]) (["a", "b"]) ([true, false]));
In Haskell do
notation can be used to hide the nested function calls. However, do
is a compile time technique that Javascript lacks.
Generator functions seem to be a good fit, but they don't work with monads that supply a priorized choice. So I have been looking for an alternative for quite some time and recently came up with a sort of monadic applicator in order to untangle the nested computation a bit:
const chain = fm => xs =>
xs.reduce((acc, x) => acc.concat(fm(x)), []);
const of = x => [x];
const id = x => x;
const infixM3 = (w, f, x, g, y, h, z) =>
f(x_ =>
w(x_, w_ => g(y_ =>
w_(y_, w__ => h(z_ =>
w__(z_, id)) (z))) (y))) (x);
const mainApp = xs => ys => zs => infixM3(
(x, k) =>
x === 0
? []
: k((y, k) =>
k((z, k) => [[x, y, z]])),
chain, xs,
chain, ys,
chain, zs);
console.log("run to completion",
mainApp([1, 2]) (["a", "b"]) ([true, false]));
console.log("short circuiting",
mainApp([0, 2]) (["a", "b"]) ([true, false]));
The applicator mimics chain in infix position hence the name. It is based on local continuations, therefore the lifted function expects a pair of arguments constisting of the bound value and the continuation respectively. If the continuation isn't applied the computation short circuits. It looks complicated but is rather a mechanical process.
Comparing the explicit version with the abstracted one I think this is an improvement in terms of readability:
chain(x =>
x === 0
? []
: chain(y =>
chain(z => [[x, y, z]]) (zs)) (ys)) (xs);
infixM3(
(x, k) =>
x === 0
? []
: k((y, k) =>
k((z, k) => [[x, y, z]])),
chain, xs,
chain, ys,
chain, zs);
The continuations in the lifted function bother though as well as the fact that the applicator is arity aware. Besides it doesn't look like do-notation at all. Can we get closer to a syntax that resembles do
notation?