Typesafe Error Handling in Kotlin
kotlin.christmas
kotlin.christmas
https://doc.rust-lang.org/std/convert/trait.From.html
This is my #1 thing with rust.
Is a stroke of genius to have a clear API for make conversions.
And I say that having developed in Scala for years and years.
Of course there's still sometimes when you want processing of other items to proceed, even though this one has failed. And that's where Try is really essential. So that's where we use it only: at the topmost level.
It's only at that top level that we wrap the call in a Try. And that works rather well.
One reason to do it like this is that we want to leverage the design philosophy of Kotlin. And not be writing Scala or Haskell in Kotlin ). And Kotlins philosophy when it comes to exceptions turns out to be: exceptions. Which is apparent in Kotlin coroutines, channels and flows.
) that's also why we try to stay away from Arrow
Truth is, I rather be writing Rust, but I need the JVM sometimes. ;)
That's the advantage of the monadic/functorial approach: they don't!
[1] https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run-catc...
Still, it's "python xmas!" all year long would be a nice theme for a blog :)
What really drives me nuts is when the same person shits on checked exceptions and then advocates for using a monadic return value. They're completely isomorphic. The only difference is in how it reads.
Checked exceptions just lead to interface pollution. You really don't know what type of exception can be thrown by an implementation of a facade when you are defining the facade. And so then you are forced to define a InterfaceOperationException and then force all callers to wrap their exceptions in this type. Even if you use an implementation that is exception-free, you are forced to catch.
Painful and elaborate ceremony for very little gain. Usually exception handling is centralised at a few places in the code. Catching exceptions at these areas and boundaries is the best place instead of littering handler code all over the code base.
Oracle has realised that checked exceptions are a mistake. None of the java functional interfaces declare checked exceptions. If your operation throws a checked exception, it cannot be used as a convenient method reference lambda. It does not decay into a standard functional interface. This alone is an ultra critical-strike against checked exceptions.
If a condition is so important that you need to declare it as a checked exception, it generally indicates it should have been be a return type or your functional granularity is large. And normal types, unlike exception types, are well-suited to generics.
The Java-style checked exception haters might see that as the best of all worlds.
I generally agree that Swift is a little nicer than Kotlin. I like the enums better than sealed classes, I like that Swift has value types.
The only think from Kotlin that I miss in Swift is the scoping methods: apply, with, let, also.
Is that true? I was under the impression that annotating a certain type of exception being thrown doesn't prevent other (i.e. unchecked) exception types from being thrown from a method. If that's true, then that's a pretty significant different between monadic returns and checked exceptions; in the former case, returning an unknown error type leads to a compiler error, but in the latter, it leads to a crash at runtime.
Having this (together with pattern matching) as the-way-to-do-errors in Kotlin is one of the main reasons I feel it fixes what Java cannot (easily) fix.
Can you articulate how it increases readability?
https://docs.swift.org/swift-book/LanguageGuide/ErrorHandlin...
fun divide(dividend: Int, divisor: Int): Try<Int, String> =
when (divisor) {
0 -> Try.Err("Cannot divide by zero!")
else -> Try.Ok(dividend / divisor)
}
The first branch of `when` is of type `Try<Nothing, String>`, the second branch is `Try<Int, Nothing>`, and the return value is supposed to be `Try<Int, String>`. None of those line up...How is that possible? Does the compiler do some magic for the `Nothing` type? The definition looks pretty damn simple:
public class Nothing private constructor()''' val personName = person.name ?: throw IllegalArgumentException("Name required") '''
Where the type of personName is now String, cause if name was null it would throw an exception (and the type of a throw expression is Nothing).
So crediting Swift for this is not fair.
This was around in Haskell before Swift existed.