language design – Why do “checked exceptions”, i.e., “value-or-error return values”, work well in Rust and Go but not in Java?

From a scientific point of view, checked exceptions can be seen as alternative return values, e.g.

Exactly. They can be seen that way, and they should be but they aren’t.

Using an Error type like is common in Rust, Elm, Haskell, and in some sub-communities in Scala or a special error value as in Go is just an alternative return value indicated in the type system. A checked exception is like an alternative return value, but it doesn’t use the normal way of returning values, it is a completely separate, very different way of “returning values”. It also sits outside of the type system, and bolts on a completely separate “checked exception” system onto the type system.

But most importantly, it is not just an alternative return value, it is also an alternative control flow.

Another problem with the specific way checked exceptions are implemented in Java, is that they are anti-modular. That is, however, not a fundamental problem of checked exceptions, unlike the ones I mentioned above. There is an idea of Modular Anchored Exceptions, for example, where you can specify something like

int foo() throws like bar { return bar(); }

And you don’t have to know (and leak!) which precise exceptions bar can throw. You can even do something like throws like bar except ArrayOutOfBoundsException when you are handling some errors yourself.