alephcloud / hs-aws-kinesis-client

A producer/consumer client library for Kinesis
http://hackage.haskell.org/package/aws-kinesis-client
Apache License 2.0
5 stars 3 forks source link

breaking change proposal re: errors #17

Closed jonsterling closed 9 years ago

jonsterling commented 9 years ago

Requiring code that calls the Kinesis producer (or consumer) to live inside MonadError ConsumerError or MonadError ProducerError is honestly too much to swallow. Makes things very difficult to compose. Originally, I'd sort of designed it as a little DSL where you wouldn't even have to refer to the producer or consumer object by name, but that ship sailed a long time ago; it just doesn't scale up.

The obvious way forward is to return something like EitherT ConsumerError m a in the calls to the producer and consumer (or just m (Either ConsumerError a)). Alternative proposals are welcome.

gregwebs commented 9 years ago

I am a big fan of m (Either l r) although I could see how an EitherT might add convenience in this case.

jonsterling commented 9 years ago

Yes, I am coming to think that m (Either l r) provides a good sweet spot, which is readily composed with other code without introducing too many constraints on the environment. Thanks for your input @gregwebs!

larskuhtz commented 9 years ago

I think that code that doesn't use anything but left and right from EitherT, could as well be kept in MonadError, since there is really no reason to not just use throwError and return. The benefit is that it seamlessly composes with Either and EitherT (and also Except, ExceptT, and even the maybe flavored monads, if really needed).

Also, I switch to preferring ExceptT over EitherT, since it is in transformers now and widely portable without introducing extra dependencies from the Edward Kmett universe.

larskuhtz commented 9 years ago

ExceptT (transformers) dependencies:

library
  build-depends: base >= 2 && < 6

EitherT (either) dependencies:

library
  build-depends:
    base              >= 4       && < 5,
    bifunctors        >= 4       && < 5,
    exceptions        >= 0.5     && < 0.9,
    free              >= 4.9     && < 5,
    monad-control     >= 0.3.2   && < 1.1,
    MonadRandom       >= 0.1     && < 0.4,
    mtl               >= 2.0     && < 2.3,
    profunctors       >= 4       && < 5,
    semigroups        >= 0.8.3.1 && < 1,
    semigroupoids     >= 4       && < 5,
    transformers      >= 0.2     && < 0.5,
    transformers-base >= 0.4     && < 0.5
gregwebs commented 9 years ago

@larskuhtz I have found difficulty in convincing the compiler that a MonadError is in fact an EitherT. I will ask for your help next time.

larskuhtz commented 9 years ago

@gregwebs that would be interesting. As long as there is an MonadError instance for EitherT the compiler should have really no trouble to instantiate anything with MonadError.

In my experience things usually only get painful when doing things like (a) changing the error type (although there a a few tricks that are not too bad) or (b) passing around functions with a parameterized and constraint context on the return type.

jonsterling commented 9 years ago

Thanks for the note about ExceptT; I repeatedly forget about this one... If I end up using a transformer, I'll use ExceptT, though I am leaning toward what Greg suggested before...

larskuhtz commented 9 years ago

I found that most of the time, the only functions from EitherT that I am missing for ExceptT are fmapLT and exceptT. Both trivial enough to just define them in place and don't really justify the extra dependencies of EitherT.

jonsterling commented 9 years ago

Resolved by #20