continuing an old thread :upside_down_face: why do...
# arrow
f
continuing an old thread 🙃 why does arrow match on
Copy code
is VirtualMachineError, is ThreadDeath, is InterruptedException, is LinkageError -> false
instead of
Copy code
is Error, is InterruptedException -> false
? and why isn't CancellationException included in the list of exceptions that is rethrown? https://kotlinlang.slack.com/archives/C5UPMM0A0/p1592481003399300?thread_ts=1592477439.391400&cid=C5UPMM0A0
mm I see Cancellation is rethrown throughout arrow due to this
Copy code
public actual fun NonFatal(t: Throwable): Boolean =
  when (t) {
    is ControlThrowable, is CancellationException -> false
    else -> true
  }
but why is it the only one listed here 🫠 now it seems like any other throwable including InterruptedException, TimeoutCancellationException etc is not rethrown, is that really correct?
w
TimeoutCancellationException
extends
CancellationException
f
ah, 'tis true
what I'm really wondering is, if I was to write a custom runCatching method that behaves more like arrows Either.catch, what would the statement for what to rethrow vs keep look like?
eg would this be enough or should it throw more exception in addition to cancellationexception? (eg what about interruptedexception?)
Copy code
public inline fun <R> runCatching(block: () -> R): Result<R> {
   return try {
      Result.success(block())
   } catch (t: Throwable) {
      when (t) {
         is Error -> { throw t } //never catch any Errors
         else -> { //t is Exception
            if (t is CancellationException) {
               //never catch CancellationException
               throw t
            } else {
               //what remains are Exceptions which are safe to catch
               Result.failure(t) 
            }
         }
      }
   }
}
w
My guess is that Arrow doesn't rethrow all
Error
subclasses because anyone can make one. I've worked with a library (can't remember which) that had their own
Error
subclass, and it certainly wasn't fatal. The reactor project seems to have made a similar decision, only considering
VirtualMachineError
,
ThreadDeath
, and
LinkageError
to be fatal (see
reactor.core.Exceptions#isJvmFatal
).
If I were trying to implement something like that, I would probably just use Arrow's
NonFatal()
function unless I had a good reason to do something else.
If you didn't know (I only found this a couple weeks ago), kotlin coroutines includes
kotlinx.coroutines.runInterruptible
which handles
InterruptedException
as a cancellation.
f
coroutines never cease to boggle me, what's the use case for that?
+ re libraries using non-fatal Error, just... wha... I can't even 🫠
s
In Java code over the decades, (checked) Exceptions have been heavily misused, used for anything.... they lost all their meaning to be able to be handled correctly and/or consistently.
f
agreed
s
TimeoutCancellationException
extends
CancellationException
They're going to change this though, but it's a big breakage so not entirely sure how they're going to deal with that. Ktor has a custom exception for this to not rely on KotlinX's
TimeoutCancellationException
but IIRC there are some edge cases where
withTimeout
doesn't work for Ktor 🤕
@Fred Friis where did you get that btw?
CancellationException
is being rethown. https://github.com/arrow-kt/arrow/blob/main/arrow-libs/core/arrow-core/src/jvmMain/kotlin/arrow/core/NonFatal.kt
f
@simon.vergauwen yeah i got it twisted 🙂 but basically the question is in addition to cancellationexception (and timeoutcancellationexception) and all errors, what else should be rethrown/is arrow rethrowing?
(because arrow does rethrow all errors, no?)
s
I guess it depends 😅 It never swallows any exceptions, but in some APIs that capture exceptions for FFI reasons like
arrow.core.raise.catch
or
Either.catch
it shouldn't catch fatal exceptions.
f
ahaha but if arrow didn't exist and it was forbidden by divine law, what would you include in a method like
Copy code
public inline fun <R> saferRunCatching(block: () -> R): Result<R> {
   return try {
      Result.success(block())
   } catch (t: Throwable) {
      when (t) {
         is Error -> { throw t } //never catch any Errors
         else -> { //t is Exception
            if (t is CancellationException) {
               //never catch CancellationException
               throw t
            } else {
               //what remains are Exceptions which are safe to catch
               Result.failure(t) 
            }
         }
      }
   }
}
s
On JVM?
Copy code
is VirtualMachineError, is ThreadDeath, is InterruptedException, is LinkageError, is CancellationException
I would not rethrow all
Error
for reasons mentioned above. This is also what you an find in Project Reactor in addition of
InterruptedException
and
CancellationException
but if memory serves me right
InterruptedException
&
CancellationException
also get a different treatment in Project Reactor but they're not considered in the isJvmFatal predicate. It's also the same as what Scala does in NonFatal.
In Multiplatform you need to use
expect/actual
and have a slightly more complex implementation as we have in Arrow.
f
on jvm backend services yeah I was gonna ask but what about eg outofmemoryerror but I see it's a virtualmachineerror