-
Notifications
You must be signed in to change notification settings - Fork 455
[#153] Control NonFatal Throwables #1305
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
Conversation
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 great. thanks so much @kartoffelsup ! 🎉
I left some comments to improve the boilerplate an enrich the MonadThrow type class so all data types befit from these changes individually as projected extensions and you can remove the boilerplate in abstract places where you are an instance of MonadThrow
already.
@@ -36,7 +43,11 @@ interface ApplicativeError<F, E> : Applicative<F> { | |||
try { | |||
just(f()) | |||
} catch (t: Throwable) { | |||
raiseError<A>(recover(t)) | |||
if (NonFatal(t)) { |
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.
We can encapsulate this pattern For All MonadThrow<F>
which constitutes most of the changes in this PR.
If you place this function as part of the MonadThrow type class
fun <F, A> Throwable.raiseNonFatal(): Kind<F, A> =
if (NonFatal(this)) raiseError<A>(this) else throw e
This functions is scoped to the MonadThrow receiver and will be available in all instances of all the data types as extension functions and would allow you to remove the if else boilerplate in most cases below.
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.
Thanks for the review and the great suggestion. :) I've applied it to all but IOBracket, it didn't work there (I guess because it's not running in the MonadThrow context?)
There is also a AbstractMethodError in IOTest MonadDefer bind: binding failure
that I can't quite figure out.
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.
Change the genError code to throw something else instead, maybe that's the issue. I'd add a new generator that throws one of the fatal errors and test for 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.
After updating graalvm to rc12 (my jdk), the test no longer fails.
Should I add the fatal error test to MonadDefer laws?
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'd add a MonadThrow laws altogether that have all MonadErrorLaws, put that test in them, and make MonadDefer do MonadThrowLaws instead of MonadErrorLaws.
That way we have tests where the functions are defined in the hierarchy :D
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.
Sorry, I'm kind of lost now. :D I've added the suggested function raiseNonFatal
to MonadThrow.
What else can I do? (and how can I/should I test it as MonadThrow Law?)
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.
@kartoffelsup this should cover it:
- override the two functions above using
raiseNonFatal
to implement them inMonadThrow
- Add a new law in the
arrow.test.laws.MonadErrorLaws
Monad Error Laws: NonFatal is caught
- Add a new la in the
arrow.test.laws.MonadErrorLaws
Monad Error Laws: Fatal errors are thrown
- Add kdoc and a
kotlin:ank:playground
snippet similar to the one inFunctor
that illustrates the usage of NonFatal in theNonFatal
object.
That should do it.
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.
@raulraja thanks for the guidance!
Regarding the catch overrides, atm I've already overridden them in ApplicativeError like so:
fun <A> catch(recover: (Throwable) -> E, f: () -> A): Kind<F, A> =
try {
just(f())
} catch (t: Throwable) {
if (NonFatal(t)) {
raiseError<A>(recover(t))
} else {
throw t
}
}
fun <A> ApplicativeError<F, Throwable>.catch(f: () -> A): Kind<F, A> =
catch(::identity, f)
Do we really need the override in MonadThrow?
Also, it looks to me that the catch(recover: (Throwable) -> E, f: () -> A)
variant can't be done with raiseNonFatal
as I am unable to call #map
on it for the recover
function.
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.
no, if you already did it there it's fine how you did it.
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 applied the changes you requested. Please let me know if there is anything else I can do. Thanks again for the help!
@@ -61,7 +62,11 @@ internal object IOBracket { | |||
val fb = try { | |||
use(a) | |||
} catch (e: Throwable) { | |||
IO.raiseError<B>(e) | |||
if (NonFatal(e)) { |
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.
e.raiseNonFatal()
modules/effects/arrow-effects-data/src/main/kotlin/arrow/effects/typeclasses/Async.kt
Outdated
Show resolved
Hide resolved
modules/effects/arrow-effects-data/src/main/kotlin/arrow/effects/typeclasses/MonadDefer.kt
Outdated
Show resolved
Hide resolved
modules/effects/arrow-effects-data/src/main/kotlin/arrow/effects/typeclasses/MonadDefer.kt
Outdated
Show resolved
Hide resolved
...effects-data/src/main/kotlin/arrow/effects/typeclasses/MonadDeferCancellableContinuations.kt
Outdated
Show resolved
Hide resolved
ddfa5de
to
e11753e
Compare
Looks like reactor Flowable is not stack safe https://app.bitrise.io/build/73c812fbc6ef9620. |
I'll rerun the build and if it passes we will leave that for another PR |
@pakoito any idea what is going on here and why this has not surfaced before? https://app.bitrise.io/build/288ec840d3cdc0ca |
None of the Rx constructs are stack-safe unless you introduce an asynchronous jump. It was surfaced, I opened a ticket and it got closed and all ReactiveX/RxJava#6322 |
Looks like that is intended there? https://arrow-kt.io/docs/integrations/rx2/#stack-safety, the comment above states this is what's supposed to happen. But the StackOverflow was caught previously. @raulraja I've annotated the StackoverFlow sample with ank:fail, all green now. |
As discussed in https://kotlinlang.slack.com/archives/C5UPMM0A0/p1550319070097800
Port of the Scala NonFatal.
I've hopefully found all places where it should be applied. I've searched with
catch \(.*:\s+Throwable\)
in all Production Files using IntelliJ.Closes #153