Fred Friis
03/14/2023, 1:45 PMSam
03/14/2023, 1:57 PMResult
only has one generic parameter, where Either
has two. That means that Either
can model everything that Result
can, but Result
cannot model everything that Either
can. If you need type information about your errors, Result
will not help you.Fred Friis
03/14/2023, 1:58 PMThe first and foremost use of exceptions in Kotlin is to handle program logic errors. Exceptions are used to check preconditions and invariants in code that static type system cannot track down during compilation. For example, if you a have a function that updates order quantity that must be always positive by the business logic of the code, then you might write something like:
/** Updates order [quanity], must be positive. */
fun updateOrderQuanity(orderId: OrderId, quantity: Int) {
require(quantity > 0) { "Quantity must be positive" }
// proceed with update
}
Calling this function with a negative or zero quantity indicates a logic error in some other place in the code and throws an exception. The same happens when you try to get an element by an out of range index from a list, divide by zero, etc.
(https://elizarov.medium.com/kotlin-and-exceptions-8062f589d07)Sam
03/14/2023, 2:08 PMEither
going anywhere. It has many use cases that aren’t addressed by the Result
type.Fred Friis
03/14/2023, 2:11 PMSam
03/14/2023, 2:17 PMEither
then my Left
type is typically some very descriptive sealed type that is not a Throwable
. The use case for Result
, where you want to use a Throwable
but not throw it, is in my experience a fairly small intersection. It comes up occasionally in framework code, where you want to shuttle an exception from one place to another. This is actually what it was designed for (specifically for the behind-the-scenes implementation of coroutines). But in my experience it isn’t typically relevant for day-to-day code.Fred Friis
03/14/2023, 2:25 PM@GET
fun getUser(id: Int) : User {
return userService
.getUser(id) //Either<GetUserError, User>
.fold({200(it)}, {when UserNotFound 404 etc etc map other errors to correct status code})
}
this is my understanding of an example of idiomatic use of Eitherpakoito
03/14/2023, 2:29 PMFred Friis
03/14/2023, 2:42 PMsimon.vergauwen
03/14/2023, 2:53 PMimo, exceptions should be just that, exceptional💯 this
CLOVIS
03/14/2023, 3:13 PMResult
is really not great. runCatching
catches CancellationException
and breaks coroutines, and as the others have said it's untyped.Fred Friis
03/14/2023, 3:17 PMSam
03/14/2023, 3:21 PMCLOVIS
03/14/2023, 3:21 PMCancellationException
is used internally by coroutines. If you don't know exactly what you're doing, you should not touch it (not catch it, not rethrow it…). Having a catch(e: Exception)
or catch(e: Throwable)
in your code can create memory leaks or break coroutines in other ways.
Arrow (at least, the newer versions) also use CancellationException
internally, and risk being broken too.
runCatching
has a catch (e: Throwable)
, and should therefore not be used in coroutines.
Either.catch
is clever and doesn't have this problem (safe to use with coroutines).Fred Friis
03/14/2023, 3:23 PMCLOVIS
03/14/2023, 3:24 PMcatch (e: Throwable)
or catch (e: Exception)
has a very bad smell. But then again, it already has a bad smell in regular code.
As long as you always catch specifically the type of exception you expect, or use clever functions like Either.catch
, you don't have to think about it.Fred Friis
03/14/2023, 3:34 PMCLOVIS
03/14/2023, 3:34 PMFred Friis
03/14/2023, 3:34 PMCLOVIS
03/14/2023, 3:37 PMSam
03/14/2023, 3:38 PMeffect
scopes that allow you to exit early without a return, hence exceptions are required for correct control flow. Any code that catches broad categories of throwables risks catching these control flow exceptions; this is an unavoidable trade-off of the functionality.CLOVIS
03/14/2023, 3:40 PMrunCatching
's documentation, but they haven't replied yet.Fred Friis
03/14/2023, 3:42 PMrunCatching
or try catch (all exceptions)
, is that enough to make this a non issue or..?CLOVIS
03/14/2023, 3:44 PMCancellationException
without knowing exactly what you're doing, you can break Coroutines and Arrow. Coroutines and Arrow themselves are written by people who know what they are doing, and are very careful to use it correctly.Fred Friis
03/14/2023, 3:44 PMCLOVIS
03/14/2023, 3:45 PMCancellationException
, you have nothing to worry about, yes.nonFatalOrThrow
is peppered through code that interacts with coroutinesCLOVIS
03/14/2023, 3:47 PMNonFatal
should have been a part of Coroutines itself. It's great that Arrow makes it so easy though 🙂Fred Friis
03/14/2023, 3:48 PMCLOVIS
03/14/2023, 3:49 PMFred Friis
03/14/2023, 3:53 PMSam
03/14/2023, 3:54 PMCLOVIS
03/14/2023, 3:55 PMFred Friis
04/26/2023, 3:12 PMinside a coroutine, yeah you need to be careful if you are using the cancellation mechanism, but we're not really doing that. we're not generally launching our own coroutines. ktor provides us the coroutine per request, so we can easily operate within that. where we use run catching is to trap a database error for example.I'm simply not familiar enough with it all to know if the above is fine or whether it could potentially result in the issues discussed in the thread (even then, arguably, the potential for issues is pretty small, no? especially if the machine isn't struggling, then there would be little reason for the parents/scheduler to kill children coroutines etc)
CLOVIS
04/26/2023, 3:28 PMrunCatching
, when used incorrectly (and if you're using it for error management, you are using it incorrectly) breaks coroutines and every other exception-based library. It doesn't matter if it's inside Ktor or anywhere else.Either.catch
, a specific try…catch
with a specific exception type (do NOT catch Throwable, Exception or RuntimeException!), or just let it bubble up normally and use Ktor's StatusPages plugin to convert it into a proper HTTP error code.Fred Friis
04/26/2023, 3:37 PMCLOVIS
04/26/2023, 3:38 PMrunCatching
.