fp-ts-fluture
fp-ts-fluture copied to clipboard
Add ability to use ConcurrentFuture as a monad
Without changing semantics and existing type you could have it as a separate field, like below:
const URI = concurrentFuture.URI;
export const monad: Monad2<typeof URI> & MonadThrow2<typeof URI> => ({
URI: URI,
of: concurrentFuture.of,
ap: concurrentFuture.ap,
map: concurrentFuture.map,
chain: (fa, f) => {
return Fluture.Par(Fluture.chain(flow(f, Fluture.seq))(Fluture.seq(fa)));
},
throwError: (e) => Fluture.Par(future.left(e))
});
If ConcurrentFuture were to be a monad, then ap (mx) (mf) = chain (f => map (f) (mx)) (mf)
would no longer hold, breaking one of Fantasy Land's guarantees.
To me, keeping adherence to this guarantee is a good thing. As soon you add Monad to ConcurrentFuture, users* will expect to be able to refactor* any code using ap
to code using chain
and map
as shown above without affecting semantics, while in reality it alters the behaviour of their code.
A solution would be to implement this function but without calling it monadic. Maybe chainSeq
? That would have my preference, however, I believe @gcanti has a different view on this matter: https://github.com/gcanti/fp-ts/issues/1173 and https://github.com/gcanti/fp-ts/issues/1150#issuecomment-593826527. TaskEither has a concurrent applicative implementation, breaking the above mentioned guarantee. So he may not mind implementing Monad for ConcurrentFuture, but it does go against the spirit of the underlying type.
* It's not really about users, refactoring code. It's mainly about abstractions that were written with this principle in mind, which break down under these unexpected semantics.
@Avaq I didn't add a monad instance to respect the original design