| 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<T, E> 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. |
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.