Hacker News new | ask | show | jobs
by zokier 16 days ago
The problem with checked exceptions afaik was far more in the execution than in the idea itself. And also late 90s-early 00s was different time in general.
1 comments

Java just makes them hard to use. They're not fully apart of the type system and they're hard to escape when you actually want to panic. Everyone around here praises Rust's result, checked exceptions are the same idea:

    fn someFn() -> Result<T, E>
    T someFn() throws E
    fun someFn(): T | E  // Kotlin's proposed error unions

Checked exceptions actually compose a little better when you have a function that can throw multiple types:

    T someFn() throws E, F, G
This is like a union type of E | F | G. I don't know about Rust, but most languages won't let you do that over generic types like Result<T, E | F | G>.

The main problem for Java's checked exceptions is just how boilerplatey they are, especially when you can't handle something. In Java if you need to become "unchecked" or panic you need to:

    try {
        someFn();
    } catch (SomeException ex) {
        throw new RuntimeException(ex); // dunno panic
    }
Ideally that would just be:

    someFn()!!!!; // shut up compile panic if this happens
Rust makes you define an enum of E, F, and G, but also provides a conversion API so you can pass any of the three and it feels like it does, at least at the site of returning the error.

It also provides an error interface so sometimes you don’t need the enum, if all the types return that interface.

Wouldn't you lose a little compile time safety a little by returning the interface, like catching Exception?

i.e. as types you don't know about get introduced the compiler won't stop bad things from happening:

    catch (Exception ex) {
        switch (ex) {
            case SomeException1 se1 -> ..
            case SomeException2 se2 -> ..
            default -> throw new IllegalStateException(ex); // panic
        }
    }
Depends on what you mean by "safety," what this is really about is open vs closed set. An interface means that there's an open set of things that could be returned, whereas an enum is a closed set. Which one is correct for you depends on your code and requirements.

It's true that if you return an open set of things, you'll have to handle cases you didn't explicitly account for.

I just meant knowing what errors can be happen at compile time vs unchecked errors flying about. I personally am not a fan of the compiler not stopping me when a new error type is introduced. Correctness is probably the better word.
> // dunno panic

This is exactly why Java is such a pain to work with.

Somehow, Java developers all decided to stop dealing with error conditions and just crash the stack whenever something weird happens.

The way Java developers seem to work these days has a lot in common with Rust beginners that just `?` or `.unwrap()` every single fallible method. Random crashes ("RuntimeException") are acceptable, so nobody even bothers doing error handling any more.

Even the base SDK doesn't really bother with handling exceptions (i.e. the story with streams + exceptions). It's an excellent language feature tainted by a combination of bad choices twenty years ago and a weird culture shift in error handling.

There’s plenty of errors that aren’t able to be handled. But yes I agree most developers don’t understand exceptions. They seem to be scared of them tbh and there’s lots of bad advice floating around about them. Like “exceptions should be exceptional” or “don’t use exceptions for control flow”. I am beginning to see a culture shift as the old guard dies out though.
The biggest problem is that you can't abstract over them. Try to write Array#map in Java, you can't - you have to write copy-paste variants for 0, 1, 2... different types of exception until you get bored.
Yeah! I eluded to that. Exceptions aren’t fully in the type system. I’m hopeful that they’ll eventually let exceptions be some sort of union type fully. Currently, they’re unions in throws clauses and catch clauses.

Personally for me it’s not that big of a deal. The thing I want the most is having null in the type system.

Some Rust libraries have started to implement unioning multiple error types and handling a subset of them while propagating the rest. But as far as I know, the idea hasn't caught on. Here are the crates I know of.

https://github.com/komora-io/terrors

https://github.com/mcmah309/eros