fp-ts-fluture icon indicating copy to clipboard operation
fp-ts-fluture copied to clipboard

Add ability to use ConcurrentFuture as a monad

Open pbadenski opened this issue 4 years ago • 2 comments

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))
});

pbadenski avatar May 21 '20 08:05 pbadenski

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 avatar May 21 '20 11:05 Avaq

@Avaq I didn't add a monad instance to respect the original design

gcanti avatar May 21 '20 12:05 gcanti