Live data from Hacker News

Kotlin's rich errors: Native, typed errors without exceptions

cekrem.github.io

11–20 of 84 posts

Re: Kotlin's rich errors: Native, typed errors without exceptions

#11

These remind me of checked exceptions in Java. Ironically, Kotlin removed checked exceptions because they tend to be annoying more than useful: there's no clear guideline to whether an exception is checked or unchecked, some functions like IO and reflection have them while others don't, they're verbose especially when closures are involved, and lots of functions simply catch and rethrow checked exceptions in unchecke…

The plain Java equivalent of the proposed semantics would be a type system extension similar to JSpecify: @Result(ok=Ok.class, error={Checked1.class, Error2.class}) Object function() combined with enough restrictions on the usages of results of such methods (e.g., only allow consuming the results in an instanceof pattern matcher; not even switch would work due to impossibility of exhaustiveness checking).

The one feature that the proposed Kotlin error types share with Java checked exceptions is that they can be collected in unions. However, the union feature for checked exceptions is pretty much useless without the ability to define higher order functions that are generic over such unions, which is why checked exceptions fell out of favor with the spread of functional APIs in Java 8.

Re: Kotlin's rich errors: Native, typed errors without exceptions

#12
The discussion around checked vs unchecked exceptions always comes down to ergonomics vs safety.

Having worked extensively with Node.js (callback hell, then Promises), I appreciate how error-as-value patterns force you to think about failure cases at every step. But the reality is most developers don't - they either:

1. Ignore the error case entirely (leading to silent failures) 2. Bubble everything up with generic error handling 3. Write defensive code that becomes unreadable

Rust's Result with the ? operator found a sweet spot - you have to acknowledge errors exist, but the syntax doesn't make it painful. The key innovation is making the happy path concise while forcing acknowledgment of errors.

For Kotlin specifically, I'm curious how this interops with existing Java libraries that throw exceptions. That's always the challenge with these proposals - they work great in greenfield code but break down at library boundaries.

The real question: does this make developers write better error handling code, or just more verbose code? I'm cautiously optimistic.

Re: Kotlin's rich errors: Native, typed errors without exceptions

#13
post #10

This is nice, and I develop often in Kotlin, but none of this will really achieve what people want so long as any line can possibly throw a runtime exception.

I think that the problems with unchecked exceptions are due to the simultaneous presence of both checked and unchecked exceptions. The designers must have thought that checked exceptions would be the rule, but left an escape hatch.

If there were no checked exceptions to begin with, people might have thought about making the Java compiler (and later language server) infer all possible exception types in a method for us (as effect systems do). One could then have static analysis tools checking that only certain exception types escape a method, giving back checked exceptions without the type and syntax level bifurcation.

On the other hand, if all exceptions were checked, they would inevitably have had to implement generic checked exception types, ironically leading to the same outcome.

Re: Kotlin's rich errors: Native, typed errors without exceptions

#14

Rich Errors look promising to me, but what about interop with Java? What will the return-type of a function be on the Java side be, if the return type on Kotlin side is: Int | ParseError | SomeOtherError? Background: unions aren't restricted to one normal type and one error type, but to one normal type and any number of error types, so this can't be modelled as syntactic sugar on top of an implicit Either/Result type…

They only care about Java -> Kotlin integration. Not the other way around. It has been like this for a long time. Looks like an extractive relationship to me to be frank.

Re: Kotlin's rich errors: Native, typed errors without exceptions

#15

These remind me of checked exceptions in Java. Ironically, Kotlin removed checked exceptions because they tend to be annoying more than useful: there's no clear guideline to whether an exception is checked or unchecked, some functions like IO and reflection have them while others don't, they're verbose especially when closures are involved, and lots of functions simply catch and rethrow checked exceptions in unchecke…

Exceptions are cheap on the happy path and super expensive on the error path. Checked exceptions only make sense for errors that are relatively common (i.e., they aren't really exceptional), which calls for a different implementation entirely where both the happy path and the error path have around the same cost. This is what modern languages like Rust and Go do as well (and I think Swift as well though don't quote m…

> Rust and Go call these panics but they are implemented like exceptions.

I don't know about Rust, but a very important difference between Java exceptions and Go panics, is that a Go panic kills the entirely process (unless recovered), whereas a Java exception only terminates the thread (unless caught).

It's a little off-topic, but I wanted to clarify that for passer-bys who might not know.

Re: Kotlin's rich errors: Native, typed errors without exceptions

#16

The discussion around checked vs unchecked exceptions always comes down to ergonomics vs safety. Having worked extensively with Node.js (callback hell, then Promises), I appreciate how error-as-value patterns force you to think about failure cases at every step. But the reality is most developers don't - they either: 1. Ignore the error case entirely (leading to silent failures) 2. Bubble everything up with generic e…

The biggest problem is that people treat it as a dichotomy: either exceptions or error values. But that's a false dichotomy.

There would be real value in a language which would have both.

Error values are perfect for un-exceptional errors, e.g. some states of a business logic. The name that the user entered is invalid, some record is missing from the database, the user's country is not supported. Cases that are part of the business domain and that _must_ be handled, and therefore explicitly modeled.

Then there is the grey area of errors that one might expect (so not truly exceptional) but are not related to the business logic. These could be for example network timeouts, unexpected HTTP errors (like 503), etc. For those, there is often no explicit handling in the domain that makes sense. So it's convenient to just throw an exception, let it automatically "bubble" to the highest level (e.g. the HTTP controller) and just return some generic error (such as HTTP 500).

There are also truly exceptional cases, that you really shouldn't encounter in your program, such as null-dereferences, invalid array index access, division by zero, etc. These indicate a bug in the code (and might be introduced explicitly with assert-style checks). The program is in an unknown, compromised state, so there's really nothing left to do than throw an exception or panic. An error value makes very little sense in this case.

I often have the discussion with friends, why a division operator, or an array access, doesn't return a `Result` type in nice languages such as Rust? Surely, if they care about error values, then each operation that can fail, must return a `Result` rather than panic (throw an exception). It is an interesting through experiment at least.

Re: Kotlin's rich errors: Native, typed errors without exceptions

#17

The discussion around checked vs unchecked exceptions always comes down to ergonomics vs safety. Having worked extensively with Node.js (callback hell, then Promises), I appreciate how error-as-value patterns force you to think about failure cases at every step. But the reality is most developers don't - they either: 1. Ignore the error case entirely (leading to silent failures) 2. Bubble everything up with generic e…

The biggest problem is that people treat it as a dichotomy: either exceptions or error values. But that's a false dichotomy. There would be real value in a language which would have both. Error values are perfect for un-exceptional errors, e.g. some states of a business logic. The name that the user entered is invalid, some record is missing from the database, the user's country is not supported. Cases that are part…

Sounds like checked and unchecked exceptions.

I mean, this could be a syntax wrapper for java checked exceptions right?

Those are isomorphic to Result in that you must handle or propagate the error. The syntax is different, sure.

Re: Kotlin's rich errors: Native, typed errors without exceptions

#18

These remind me of checked exceptions in Java. Ironically, Kotlin removed checked exceptions because they tend to be annoying more than useful: there's no clear guideline to whether an exception is checked or unchecked, some functions like IO and reflection have them while others don't, they're verbose especially when closures are involved, and lots of functions simply catch and rethrow checked exceptions in unchecke…

I think bridging this syntax onto legacy checked exceptions from java would make a lot of sense.

Re: Kotlin's rich errors: Native, typed errors without exceptions

#19
post #11

These remind me of checked exceptions in Java. Ironically, Kotlin removed checked exceptions because they tend to be annoying more than useful: there's no clear guideline to whether an exception is checked or unchecked, some functions like IO and reflection have them while others don't, they're verbose especially when closures are involved, and lots of functions simply catch and rethrow checked exceptions in unchecke…

The plain Java equivalent of the proposed semantics would be a type system extension similar to JSpecify: @Result(ok=Ok.class, error={Checked1.class, Error2.class}) Object function() combined with enough restrictions on the usages of results of such methods (e.g., only allow consuming the results in an instanceof pattern matcher; not even switch would work due to impossibility of exhaustiveness checking). The one fea…

This last point is the key observation.

Re: Kotlin's rich errors: Native, typed errors without exceptions

#20

These remind me of checked exceptions in Java. Ironically, Kotlin removed checked exceptions because they tend to be annoying more than useful: there's no clear guideline to whether an exception is checked or unchecked, some functions like IO and reflection have them while others don't, they're verbose especially when closures are involved, and lots of functions simply catch and rethrow checked exceptions in unchecke…

Exceptions are cheap on the happy path and super expensive on the error path. Checked exceptions only make sense for errors that are relatively common (i.e., they aren't really exceptional), which calls for a different implementation entirely where both the happy path and the error path have around the same cost. This is what modern languages like Rust and Go do as well (and I think Swift as well though don't quote m…

>Exceptions are cheap on the happy path and super expensive on the error path.

Depends on the software, huh.

Post reply on HN