-
-
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 onError
and adaptError
to Applicative/MonadError
#1739
Conversation
assigning @edmundnoble as well since he is removing |
Codecov Report
@@ Coverage Diff @@
## master #1739 +/- ##
==========================================
+ Coverage 94% 95.27% +1.27%
==========================================
Files 253 265 +12
Lines 4171 4298 +127
Branches 162 99 -63
==========================================
+ Hits 3921 4095 +174
+ Misses 250 203 -47
Continue to review full report at Codecov.
|
I'm waiting for #1751 to be resolved to update this PR (probably just moving stuff to |
@@ -34,4 +34,7 @@ final class ApplicativeErrorOps[F[_], E, A](val fa: F[A]) extends AnyVal { | |||
|
|||
def recoverWith(pf: PartialFunction[E, F[A]])(implicit F: ApplicativeError[F, E]): F[A] = | |||
F.recoverWith(fa)(pf) | |||
|
|||
def onError(fa: F[A])(pf: PartialFunction[E, F[Unit]])(implicit F: ApplicativeError[F, E]): F[A] = | |||
F.onError(fa)(pf) |
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 is untested.
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.
Yes, I mentioned this in the description :)
Haven't added non-discipline tests (yet?), they all seem to be based on Option, for which none of these two functions happen to make sense
Should I add a more complicated transformer to the tests to test this?
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.
hmmm, since this is a simple delegation what about adding a simple doctest (maybe using Either
) to the method in the type class?
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.
Bear in mind that tests are missing for the implementation, not just for the syntax. I have added laws and discipline tests, but the reason I still haven't added unit tests is that all the unit tests I could find are based on Option, whereas testing this would require something like
EitherT[Writer, ....
, I don't have a problem with it, just wanting to know if it's a good idea
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.
I assumed that the discipline tests provides enough coverage for the implementation, no? Anyway I am not against adding a EitherT[Writer,...
to the unit tests
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.
Well, my idea was exactly that discipline tests might be enough, which is way I submitted this in its current form. Looking for advice from the maintainers to take the final decision
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.
yeah, I think @johnynek and I were saying that ideally we also want to cover the delegation in the syntax, and one way to do that easily is to add a doctest in the type class method declaration, which uses the syntax and can also serve as an example in the scala doc.
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.
I can't see any doctests for the other methods on ApplicativeError, apart from a scaladoc example for fromEither
. Would you happen to have an example at hand of how syntax forwarders are tested in cats via doctests ?
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.
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.
I'll add these today, especially since there's a bug 😁 (the fa
should not be a parameter, shadows the implicit class parameter)
@@ -12,4 +12,7 @@ final class MonadErrorOps[F[_], E, A](val fa: F[A]) extends AnyVal { | |||
|
|||
def ensureOr(error: A => E)(predicate: A => Boolean)(implicit F: MonadError[F, E]): F[A] = | |||
F.ensureOr(fa)(error)(predicate) | |||
|
|||
def adaptError(fa: F[A])(pf: PartialFunction[E, E])(implicit F: MonadError[F, E]): F[A] = | |||
F.adaptError(fa)(pf) |
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 is untested.
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.
Same as above
@edmundnoble @kailuowang @djspiewak Any news on this one? I'm still looking for advice on the testing, but apart from that, this would be nice to have for use with cats-effect IO, now that ApplicativeError seems to be staying in cats. |
@@ -39,6 +39,12 @@ trait ApplicativeErrorLaws[F[_], E] extends ApplicativeLaws[F] { | |||
|
|||
def attemptFromEitherConsistentWithPure[A](eab: Either[E, A]): IsEq[F[Either[E, A]]] = | |||
F.attempt(F.fromEither(eab)) <-> F.pure(eab) | |||
|
|||
def onErrorPure[A, B](a: A, f: E => F[B]): IsEq[F[A]] = | |||
F.onError(F.pure(a)){case err => F.void(f(err))} <-> F.pure(a) |
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.
Not quite sure I follow here, why not def onErrorPure[A, B](a: A, f: E => F[Unit])....
? why take a E => F[B]
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.
I've mentioned this in my comments, I couldn't get a Gen[F[Unit]]
. However, it was my first time dealing with all the discipline infrastructure, and never had to to this particular thing in my own experience with scalacheck, so glad to be proven wrong :)
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.
adding an ArbFU: Arbitrary[F[Unit]]
param to the MonadErrorTests
and ApplicativeErrorTest
methods should do the trick
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.
I think this is exactly what I did and iirc it didn't compile. Let me give it another try though :)
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.
compiled for me, EitherTests passes. running full now. update: full suite passed.
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.
Yes you're right. I hadn't realised I needed to add it to the MonadErrorTests as well (because of parent
). Thank you! (I'll update the PR tonight with the change)
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.
You are welcome. Thank you for contributing!
Also I think #1751 is close to merge. I think we can safely assume that |
@kailuowang I've simplified the discipline tests to take |
👍 LGTM, thanks very much @SystemFw |
Merging with two approvals. |
PR:
As per this Gitter conversation with @djspiewak, this PR adds two functions:
ApplicativeError#onError
, to execute a callback on certain errors and rethrow:with laws:
Use case: error handling that respects module boundaries, e.g. rollback at the db layer, and then send a failed response at the http layer
MonadError#adaptError
, to transform certain errorswith laws:
Use case: transforming errors (especially Throwable) between different layers of an application. The type is not as good as
leftMap
but the best we can have I think (except maybe constraining to Throwable).A few things I'm not sure about code-wise:
syntax
imports and trying to adhere to the existing style resulted in fairly "lispy" codeArbitrary[F[Unit]]
in the laws, had to settle forF[B].void