Norbi
03/10/2023, 9:57 AMRaise<R>
, how should I use it in "real" code?
My problem is:
1. I've found that very often I want to know the root cause of an error - so I've reinvented the cause
mechanism of exceptions 😐
2. I've found that by handling raise()
-d errors, the contextual information is lost at the top level, where I maybe want to log a stack trace for easier investigation of the problem.
Do you have some practical advice for using it?
Maybe my head has just "stuck" thinking only by exceptions 🙂
Thanks.Sam
03/10/2023, 9:59 AMsimon.vergauwen
03/10/2023, 10:00 AMUNIQUE_VIOLATION
to UserAlreadyExists
. Keeping the cause around is definitely advised if you want to capture truly exceptionally things, which is often done in something like UnkownError
.
For 2, I have this PR open. Tracing POC and I am eagerly looking for feedback. This allows you to get stack traces for Raise<E>
.CLOVIS
03/10/2023, 10:03 AMRaise<String>
. If you do this, you do indeed lose all context.
However, if you create a proper failure type for your various endpoints, any value gives you the entire context, and you can easily insert logging/breakpoints to find out what the result of any function is. It also makes I18n for errors trivial 🙂Norbi
03/10/2023, 10:11 AMoften done in something likeYes, I use it regularly like:UnkownError
sealed interface SomeFunctionError {
data class CalculationError(...): SomeFunctionError
...
data class UnknownError(val someContextAboutTheException: Any, ..., val cause: Exception): SomeFunctionError
}
So probably I should simply remove the SomeFunctionError
sub-type wrapping the exception, and create a specific RuntimeException
subclass instead?
Hmmm, it is not easy to structure the code with "two levels" of error handling.
Thanks for the advice for everyone!simon.vergauwen
03/10/2023, 10:50 AMSo probably I should simply remove theUhm, don't think that is what I meant 😅 What are you currently usingsub-type wrapping the exception, and create a specificSomeFunctionError
subclass instead?RuntimeException
UnkownError
for?
@CLOVIS you seemed a bit surprised by my comment.CLOVIS
03/10/2023, 10:52 AMsimon.vergauwen
03/10/2023, 10:56 AM@ExperimentalTracing
🙈 on it and merging it 😁
@Norbi here you can see the difference in practice.
Ktor Arrow Example wraps exceptional things in Unexpected
with a description
and carrying the cause: Throwable
. At the edge these are logged and turned into 500
.
Gh Alerts Subscription Service allows exceptional things to remain as Throwable
and only turns expected domain exceptions into UserNotFound
in the linked case. Any uncaught exceptions are resolved by Ktor into 500
.
It depends a bit on use-case to use-case, but the second approach is I think more often used. You can gradually add more typed errors for only cases you care to track in the type system. If my application cares about "Database timeout", or "Table Deadlock", etc then I can lift it into Raise<E>
but most applications don't care about that.Norbi
03/10/2023, 10:57 AMWhat are you currently usingMainly for wrapping (general) exceptions thrown by external functions when I invoke them. What I find difficult is to bridge the exception-only external world with myfor?UnkownError
raise()
-abundant own code...simon.vergauwen
03/10/2023, 10:58 AM1.2.0-RC
)