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 specificSomeFunctionErrorsubclass 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)