Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't use MonadError syntax for EitherT #2022

Closed
wedens opened this issue Nov 9, 2017 · 3 comments · Fixed by #2029
Closed

Can't use MonadError syntax for EitherT #2022

wedens opened this issue Nov 9, 2017 · 3 comments · Fixed by #2029
Assignees
Milestone

Comments

@wedens
Copy link
Contributor

wedens commented Nov 9, 2017

With introduction (#1644) of prioritised MonadError instance for F: MonadError in EitherT[F, E, A], it's now impossible to use MonadError syntax for handling E.
It makes it difficult to upgrade to 1.0.0-RC1 as this syntax is used quite a bit in a project.

implicit val merr: MonadError[IO, Throwable] = ???
type M[A] = EitherT[IO, Error, A]
val a: M[A] = ???
a.ensure(Error)(predicate) // type mismatch: found Error, required Throwable
@wedens wedens closed this as completed Nov 9, 2017
@wedens
Copy link
Contributor Author

wedens commented Nov 10, 2017

Actually I still have this issue with handleErrorWith instead of ensure.

val e: EitherT[ConnectionIO, A, B] = ???
e.handleErrorWith(1) // required: Throwable => EitherT[ConnectionIO,A,B]
// [error]  found   : A => cats.data.EitherT[doobie.ConnectionIO,A,B]
// [error]  required: Throwable => cats.data.EitherT[doobie.ConnectionIO,A,B]
//[error]         e.handleErrorWith((a: A) => EitherT.left(a.pure[ConnectionIO]))
e.handleErrorWith((a: A) => EitherT.left(a.pure[ConnectionIO]))

@wedens wedens reopened this Nov 10, 2017
@kailuowang
Copy link
Contributor

@wedens not sure I fully understand your second example, were those error caused by
e.handleErrorWith(1)
or e.handleErrorWith((a: A) => EitherT.left(a.pure[ConnectionIO]))?
Is your A abstract in this usage?

@kailuowang
Copy link
Contributor

kailuowang commented Nov 10, 2017

it's just type class syntax extension cannot differentiate the two instance available:
MonadError[EitherT[ConnectionIO, A, ?], A] and MonadError[EitherT[ConnectionIO, A, ?], Throwable], right now the latter takes higher precedence.
A quick work around before we fix in Cats would be add a high priority instance in the local scope.

implicit val me =  MonadError[EitherT[ConnectionIO, A, ?], A]
e.handleError((a:A) => someB)

I see 3 options to fix it in Cats.

  1. A relatively easy quick fix in Cats would be just flipping the priority sequence between the two instance, so that the syntax goes back to the behavior prior to 1.0-RC1. Then if user need the MonadError syntax with the other MonadError instance (namely MonadError[EitherT[ConnectionIO, A, ?], Throwable] in this example), they would need to do the same work around as above, i.e. create a local implicit instance that takes higher precedence.
  2. make the MonadError[EitherT[ConnectionIO, A, ?], Throwable] no longer implicit, if user needs it, they have to explicitly call a method to get it.
  3. duplicate all the MonadError syntax methods in EitherT that is parameterized to help type inference. E.g.
def handleError[E](f: T => E)(implicit ME: MonadError[EitherT[F, L, ?], E]) = ME.handleError(this)(f)

This is likely to get rid off the ambiguity, but I haven't test it.

cc @leandrob13 @peterneyens @LukaJCB

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants