Here is a coroutine that avoids nested patterns like (chain(m) (chain(...))
for monadic computations:
const some = x => none => some => some(x);
const none = none => some => none;
const option = none => some => tx => tx(none) (some);
const id = x => x;
const of = some;
const chain = fm => m => none => some => m(none) (x => fm(x) (none) (some));
const doM = (chain, of) => gf => {
const it = gf();
const loop = ({done, value}) =>
done
? of(value)
: chain(x => loop(it.next(x))) (value);
return loop(it.next());
};
const tx = some(4),
ty = some(5),
tz = none;
const data = doM(chain, of) (function*() {
const x = yield tx,
y = yield ty,
z = yield tz;
return x + y + z;
});
console.log(
option(0) (id) (data)); // 0
But I'm not able to implement an equivalent coroutine for applicative computations:
const some = x => none => some => some(x);
const none = none => some => none;
const option = none => some => tx => tx(none) (some);
const id = x => x;
const of = some;
const map = f => t => none => some => t(none) (x => some(f(x)));
const ap = tf => t => none => some => tf(none) (f => t(none) (x => some(f(x))));
const doA = (ap, of) => gf => {
const it = gf();
const loop = ({done, value}, initial) =>
done
? value
: ap(of(x => loop(it.next(x)))) (value);
return loop(it.next());
};
const tx = some(4),
ty = some(5),
tz = none;
const data = doA(ap, of) (function*() {
const x = yield tx,
y = yield ty,
z = yield tz;
return x + y + z;
});
console.log(
option(0) (id) (data)); // none => some => ...
This should work, but it doesn't. Where does the additional functorial wrapping come from? I guess I am a bit lost in recursion here.
By the way I know that this only works for deterministic functors/monads.
I'm not able to implement an equivalent coroutine for applicative computations
Yes, because generator functions are monadic, not just applicative. The operand of a yield
expression can depend on the result of the previous yield
expression - that's characteristic for a monad.
Where does the additional functorial wrapping come from? I guess I am a bit lost here.
You are doing ap(of(…))(…)
- this is equivalent to map(…)(…)
according to the Applicative laws. Compared to the chain
call in the first snippet, this does not do any unwrapping of the result, so you get a nested maybe
type (which in your implementation, is encoded as a function).