I translated the coroutine implementation from Control.Monad.Coroutine
to Javascript. The translation might contain a few bugs, but my actual problem is that I don't know how to put everything together to apply it.
The raw coroutine type is m (Either (s (Coroutine s m r)) r)
, where m
has a monad and s
a functor constraint:
-- | Suspending, resumable monadic computations.
newtype Coroutine s m r = Coroutine {
-- | Run the next step of a `Coroutine` computation.
-- The result of the step execution will be either a suspension or
-- the final coroutine result.
resume :: m (Either (s (Coroutine s m r)) r)
}
instance (Functor s, Monad m) => Monad (Coroutine s m) where
....
Here is the translation:
const TAG = Symbol.toStringTag;
const CoroutineT = mmx => ({
[TAG]: "Coroutine",
run: mmx
});
CoroutineT.map = ({map: mapSuspension}, {map: mapBase}) => f => function go(mmx) {
return CoroutineT(mapBase(mx => mx.run({
left: tx => Either.Left(mapSuspension(go) (tx)),
right: x => Either.Right(f(x))
})) (mmx.run));
};
CoroutineT.ap = ({map: mapSuspension}, {map: mapBase, chain}) => function go(mmf) {
return mmx =>
CoroutineT(chain(mmf.run) (mf => mx.run({
left: tf => Either.Left(mapSuspension(go) (tf)),
right: f => mapBase(mx => mx.run({
left: tx => Either.Left(mapSuspension(go) (tx))
right: x => Either.Right(f(x))
})) (mmx.run)
})));
};
CoroutineT.of = ({of}) => x => CoroutineT(of(Either.Right(x)));
CoroutineT.chain = ({map}, {of, chain}) => mmx => fmm => function go(mmx2) {
return CoroutineT(chain(mmx2.run) (mx => mx.run({
left: tx => of(Either.Left(map(go) (tx))),
right: x => fmm(x).run
})));
} (mmx);
// transformer
CoroutineT.lift = ({map}) => comp(CoroutineT) (map(Either.Right));
CoroutineT.mapT = ({map: mapSuspension}, {map: mapBase}) => f => function go(mmx) {
return CoroutineT(
mapBase(mx => mx.run({
left: tx => Either.Left(mapSuspension(go) (tx)),
right: x => Either.Right(x)
})) (f(mmx.run)));
};
CoroutineT.consume = ({of, chain}) => f => mmx =>
CoroutineT(chain(mmx.run) (mx => mx.run({
left: tx => f(tx)
right: x => of(Either.Right(x))
})));
CoroutineT.exhaust = ({of, chain}) => f => function go(mmx) {
return chain(mmx.run) (mx => mx.run({
left: tx => go(f(tx)),
right: of
}));
};
CoroutineT.exhaustM = ({of, chain}) => fm => function go(mmx) {
return chain(mmx.run) (mx => mx.run({
left: tx => chain(fm(x)) (go),
right: of
}))
};
CoroutineT.fold = ({of, chain}) => f => init => mmx => function go([acc, mmx2]) {
return chain(mmx2.run) (mx => mx.run({
left: tx => go(f(acc) (tx)),
right: x => of(Pair(acc, x))
}));
} (Pair(init, mmx));
// suspension functor
CoroutineT.mapSuspension = ({map: mapSuspension2}, {map: mapBase}) => f => function go(mmx) {
return CoroutineT(
mapBase(mx => mx.run({
left: tx => Either.Left(f(mapSuspension2(go) (tx))),
right: x => Either.Right(x)
})) (mmx.run));
};
CoroutineT.suspend = ({of}) => tx => CoroutineT(of(Either.Left(tx)));
// Yield suspension
export const Yield = x => y => ({
[TAG]: "Yield",
run: Pair(x, y)
});
Yield.map = f => tx => Yield(tx[0]) (f(tx[1]));
Yield.yield = ({of}) => x => CoroutineT.suspend({of}) (Yield(x) (of(null)));
// Either
const Either = {};
Either.Left = x => ({
[TAG]: "Either",
run: ({left}) => left(x)
});
Either.Right = x => ({
[TAG]: "Either",
run: ({right}) => right(x)
});
Either.cata = left => right => tx => tx.run({left, right});
Coroutines are a generalization of generators so implementing the following do-notation like function seems consistent (even though we're stuck with nesting again):
const _do = ({chain, of}) => init => gtor => {
const go = ({done, value: mx}) =>
done
? mx
: chain(mx) (x => go(it.next(x)));
const it = gtor(init);
return go(it.next());
};
const of = x => [x];
const chain = xs => fm =>
xs.reduce((acc, x) =>
acc.concat(fm(x)), []);
const xs = [1,2],
ys =[3,4];
_do({chain, of}) () (function*() {
const x = yield xs,
y = yield ys;
return [x + y];
});
I have no idea how to even begin with. The monad is probably Id
, because we don't need an effect for this computation. Maybe someone can give a sketch how a coroutine encoding of the above function would look like.