Hacker News new | ask | show | jobs
by ragnese 1976 days ago
What if we designed a system where there are two kinds of failures that can be returned? One where the caller is forced to address it by the compiler, and one that is transparent to the caller, but can be caught and addressed by anyone in the call stack (probably the top level)?

And one could convert one type of failure to the other. So if you call a library function and it returns the force-you-to-address kind of error, we could determine that we can't actually handle it at the call site, and just convert it to the invisible kind and let it keep going up.

The force-you-to-address it kind is enforced by the compiler. The compiler forces you to check if the function fails. A "checked failure"? "Checked error"? Hmm.

3 comments

I think everyone has had this distinction on their mind when designing error handling in the past thirty years or so, it’s just that figuring out an ergonomic way to express it is quite hard.

In some languages the distinction is between logic errors and runtime errors. In Java, checked and unchecked exceptions. In Go, err and panic. Rust also has Err() and panic!().

If you look at, say, the evolution of the “if” statement, it was a number of years before this “obvious” control structure was added to programming languages. So there might be something similarly obvious for error handling, we just haven’t figured it out yet.

For sure. I didn't mean to imply that this isn't a legitimately hard problem for language designers. Just being a smart ass.

I do think that checked and unchecked exceptions are the right way. The issues that people have with Java's checked exceptions are mostly centered around Java's particular implementation of the concept. The biggest failure of which, IMO, is that you can't write an interface that is generic over the exception type. Also, wrapping in try {} catch {} finally {} is cumbersome. But Java is just cumbersome. In some expression-oriented language, it could be smooth. `try` could become an expression that returns a value. Or you could have syntax help like something Rust-ish: `val thing = fallible().finally { cleanUp() }?`.

That's the main reason, IMO, people don't complain quite as much about Rust's Result<T, E>, which is very much like a checked exception mechanism in spirit. The only problem with the Rust approach is that you have an extra if-statement on every single call to a fallible function, to unwrap the success/failure. If it used exceptions, the happy paths would (sometimes) be more optimized, if I understand correctly.

But some things just can't be fixed at the language level. You have to craft good error types and messages. You have to think through your happy paths as well as your sad paths. I like when languages force you to think about failure. I don't like when languages only have unchecked exceptions for all kinds of failures.

> The only problem with the Rust approach is that you have an extra if-statement on every single call to a fallible function, to unwrap the success/failure. If it used exceptions, the happy paths would (sometimes) be more optimized, if I understand correctly.

This is an implementation detail, and I’m not saying that lightly—I would not be surprised if future versions of Rust eliminated the conditional, because similar optimizations have been made in e.g. Haskell, and Rust has done some interesting work to optimize the run-time representation of enum types in the past to make them work the way you would expect the equivalent C types to work (e.g. Option<&X>).

> If you look at, say, the evolution of the “if” statement, it was a number of years before this “obvious” control structure was added to programming languages

As described [0] and discussed [1] a few weeks ago. Fascinating.

[0] https://github.com/ericfischer/if-then-else/blob/master/if-t...

[1] https://news.ycombinator.com/item?id=25406211

My irony detector is buzzing. You just described err vs. panic in go.
Checked vs unchecked exceptions in Java.
IMO the only way for this to actually work is to have the language/compiler force "exceptions" to be part of function signature. I am not aware of any mainstream language which does this.

Without compiler support ANY call can end up throwing an exception and thus ANY call can end up not returning (jumping straight to handler higher up in the call stack).

> IMO the only way for this to actually work is to have the language/compiler force "exceptions" to be part of function signature. I am not aware of any mainstream language which does this.

Java. Also Swift.

Rust also counts if you squint and call Result a checked exception and panic an unchecked exception. It's a little different because you technically can ignore a returned Result- it's just a compiler warning, rather than an error. Similar for Haskell and OCaml.