-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Add MonadError instance for EitherT that recovers from F[_] errors. #1644
Changes from 2 commits
43eab48
979d869
4148d21
ca76937
aa6a955
ab37c2b
a09fe4c
6d41f14
3882f81
5eae9e4
c554233
f477698
eee4315
cd04677
3160827
d10318b
b7e1760
e7b5590
eabb6f2
bdc44a8
b5425bd
abce798
839f1be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,11 +88,22 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) { | |
|
||
def map[D](f: B => D)(implicit F: Functor[F]): EitherT[F, A, D] = bimap(identity, f) | ||
|
||
def mapF[N[_], D](fe: F[Either[A, B]] => N[Either[A, D]]): EitherT[N, A, D] = EitherT(fe(value)) | ||
|
||
def recoverF[E](pf: PartialFunction[E, Either[A, B]])(implicit ae: ApplicativeError[F, E]): EitherT[F, A, B] = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also feels weird to me because you can "recover" to the left side, which is a different semantics from the original There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kailuowang that is exactly what I intended, maybe the names should be changed so the semantics are right? The problem is that if you have an What do you mean by organizing them into two different derivations of MonadError instances? As I discussed in #1643, there is a limitation on recovering for monad transformers like EitherT because you can't recover from F (my example is with a Future). My intention is to keep it practical and since EitherT has its own implementation of recover and recoverWith which are used for overriding the corresponding function of its MonadError instace, I thought we could add these functions without including them in MonadError or some other typeclass. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So as @djspiewak pointed out in #1643, there are two ways to provide a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kailuowang right! Let me work it out then, thanks! |
||
EitherT(ae.recover(value)(pf)) | ||
|
||
def recoverFWith[E](pf: PartialFunction[E, F[Either[A, B]]])(implicit ae: ApplicativeError[F, E]): EitherT[F, A, B] = | ||
EitherT(ae.recoverWith(value)(pf)) | ||
|
||
def semiflatMap[D](f: B => F[D])(implicit F: Monad[F]): EitherT[F, A, D] = | ||
flatMap(b => EitherT.right(f(b))) | ||
|
||
def leftMap[C](f: A => C)(implicit F: Functor[F]): EitherT[F, C, B] = bimap(f, identity) | ||
|
||
def leftMapF[N[_], C](fe: F[Either[A, B]] => N[Either[C, B]]): EitherT[N, C, B] = | ||
EitherT(fe(value)) | ||
|
||
def compare(that: EitherT[F, A, B])(implicit o: Order[F[Either[A, B]]]): Int = | ||
o.compare(value, that.value) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this and the
leftMapF
feels weird to me. The type signature doesn't prevent the user frommappingchanging the other side.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kailuowang I recognize your point. It is mapping F to N. Whatever is inside, could be a Left or a Right (mapped or not) and it is still mapping the Either. If I left the F[] to N[] without mapping the Either inside it would be a transform (F[] ~> N[]) but in EitherT transform is like
Should it be a transformF?
Should we make standard what to do in a mapF? In Kleisli it looks like this:
That is why I thought I could add a mapping function from F[] to N[] in EitherT. Also, transform in Kleisli means a transformation from F to N and also maps what is inside of F. Should we fix those semantics in the monad transformers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This
transformF
sounds like we might need a new higher order type class? likeThere is some discussion in this gist. It maybe too theoretical, but I think we can see its practial usage here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kailuowang I will look into that!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kailuowang taken your advice I opened #1713 and came to the conclusion to add this to EitherT
There is something confusing about EitherT and OptionT using
transform
as a bimap/map of the parametrized types respectively. I called it transformF just to avoid the confusion. The issue is opened for the evaluation of your proposal and to address the confusion aroundtransform
semantics.