Live data from Hacker News

Why checked exceptions failed

borretti.me

141–150 of 318 posts

Re: Why checked exceptions failed

#141

Checked exceptions failed in Java because they don't play well with parametric polymorphism. Union types might help (exception list declaration is actually a union type) but I don't think it would succeed anyway as it is not general enough. Handling effects and effect polymorphism in programming languages is an active area of research and there are some new languages that try to approach the problem (ie. Koka). Haske…

No, that's not it. The same problem is true for other error-handling types, such as the "either" or "result" types. Languages without union types or a similar mechanism are unable to define the error types adhoc and force the developer to define them in advance, which is very unergonomic.

But that is true in both cases. The problems with checked exceptions come on top, namely that you cannot use all the regular value-machinery to transform and manipulate the result of a function-call.

Re: Why checked exceptions failed

#142

Earlier quoted context omitted.

This is only because you use exceptions incorrectly. You can (and should) write: try { final var fResult = f(g()); //do something with fResult } catch (E1 e) {...} catch (En e) {...} That's the main idea of exceptions in all languages: main flow is kept together and exceptional flows are separate.

I think there’s a strong assumption in this pattern that can and should be handled immediately and that there is a recovery path from the failure, if there is no recovery path and you’re just propagating the error this pattern becomes an awfully verbose return statement.

When structured exception handling was becoming popular (C++ in nineties?) the idea was that immediate error handling (like when you have to check return values for errors) makes the main flow (happy path) blurred and unreadable.

You cannot have both at the same time, I'm afraid.

Re: Why checked exceptions failed

#143

Earlier quoted context omitted.

That is not better code. Couple of try-catch-catch-catch-...-catch cases in a function that would otherwise be 2-3 lines long makes for an awful code. Also, imagine a situation when an iterator throws an exception. Or you wanted to write f(g()) but now you can't and have to do: try: T t = g(); catch E1: ... catch En: ... f(t); Now it takes a much greater effort for the reader to figure out what's going on. It's also…

This is only because you use exceptions incorrectly. You can (and should) write: try { final var fResult = f(g()); //do something with fResult } catch (E1 e) {...} catch (En e) {...} That's the main idea of exceptions in all languages: main flow is kept together and exceptional flows are separate.

I think the issue with this pattern is that either we don't care about went wrong, we want to treat all errors the same way, and then having n catch clauses is overkill. Or we do care about went wrong, and in that case the exception type is not enough to identify the issue, as often multiple lines in the try block will throw the same exception type.

Re: Why checked exceptions failed

#144
post #2

I am surprised an even more obvious interface definition of public interface ExampleInterface { void foo() throws IOException, AnotherException; } was not covered: is this also not supported? That would solve a lot of use cases if inheritance is respected.

Wouldn't it still be a problem? By putting "throws [Exception List]" in the interface, the interface making assumptions about the implementation details of every potential implementation. The interface can't know every exception that an implementation might throw, and using your example, some implementations may not use IP at all and won't throw those exceptions. It seems like you'd end up needing to list every possi…

As others have pointed out, my example would have been better if it said `ExampleExceptionFoo, ExampleExceptionBar` to more clearly indicate that an interface would define what exceptions it could throw as part of the contract. Individual implementations could extend those base exceptions if they need to.

There is not much difference between this and using error types, except the syntax sugar.

Re: Why checked exceptions failed

#145

Checked exceptions failed in Java because they don't play well with parametric polymorphism. Union types might help (exception list declaration is actually a union type) but I don't think it would succeed anyway as it is not general enough. Handling effects and effect polymorphism in programming languages is an active area of research and there are some new languages that try to approach the problem (ie. Koka). Haske…

No, that's not it. The same problem is true for other error-handling types, such as the "either" or "result" types. Languages without union types or a similar mechanism are unable to define the error types adhoc and force the developer to define them in advance, which is very unergonomic. But that is true in both cases. The problems with checked exceptions come on top, namely that you cannot use all the regular value…

> No, that's not it. The same problem is true for other error-handling types, such as the "either" or "result" types. Languages without union types or a similar mechanism are unable to define the error types adhoc and force the developer to define them in advance, which is very unergonomic

We are saying the same thing, aren't we?

But it is not only about exceptions. Other effects have the same problem and you need a mechanism to define effect polymorphic functions.

Re: Why checked exceptions failed

#146

Earlier quoted context omitted.

> The list of recoverable exceptions that can be thrown by a method should be part of the contract. If it’s really recoverable, then should it actually be an exception? How often do you see exceptions that are recoverable? > If not, then to avoid crashing you would have to catch the root Exception class, which everyone agrees is a bad idea. I disagree that it’s a bad idea, having a catch-all exception handler to avoi…

If it’s really recoverable, then should it actually be an exception? That's the problem with exceptions in general. They're a hammer that makes everything look like a nail. People start using them for all kinds of control flow situations because they're more convenient than having to deal with a lack of type system support for optional values, etc. You get to the point where you have a parser that is expecting a digi…

There are remarkably few situations involving recoverable errors, though, and they almost all look like "not found"--which includes your parse integer example--and, even then, most of the time you will want a forceful automatic exception variant and not a "recoverable" error value as there is almost never anything to do instead: try-parse is so rare of a thing that is correct to do it warrants having a quick "try" attached to the expression.

In contrast, there are a billion ways in which almost every line of code can fail--even stuff people are super sure could never fail is still usually at least subject to stack exhaustion--and almost none of these failures are things you should locally "handle"; and yet in many--even most--cases these are situations you can still recover from at a higher level if you don't screw up your own logic (which is not the point of these errors).

Re: Why checked exceptions failed

#147
post #15

Earlier quoted context omitted.

> the interface making assumptions about the implementation details of every potential implementation. No, as you'd force the implementation to catch their implementation specific exceptions and rethrow them in the way defined by the interface. That's really the crux of it, when I call foo(), I expect to see a FooException when something goes wrong, since that's the only one I can meaningful react to. If I get an Imp…

Unless the language does that for you (both enforcing it, and providing tools to make it absolutely trivial), it will never happen, it's just way too much overhead to have to define a new exception for every method in an interface, then a sub-exception for every method in the implementation of that interface, then go through the internal conversions from the underlying exceptions to the parent one. People can't be ar…

What you are saying is that people can't be arsed to do proper error checking, which is absolutely true.

Making a sane hierarchy of exceptions is the smaller of the problems of actually handling them everywhere.

Re: Why checked exceptions failed

#148
post #143

Earlier quoted context omitted.

This is only because you use exceptions incorrectly. You can (and should) write: try { final var fResult = f(g()); //do something with fResult } catch (E1 e) {...} catch (En e) {...} That's the main idea of exceptions in all languages: main flow is kept together and exceptional flows are separate.

I think the issue with this pattern is that either we don't care about went wrong, we want to treat all errors the same way, and then having n catch clauses is overkill. Or we do care about went wrong, and in that case the exception type is not enough to identify the issue, as often multiple lines in the try block will throw the same exception type.

Yes - that's one of the issues with structured exception handling. Relying only on exception type itself is not enough as information is lost (ie. the actual source of exception)

Re: Why checked exceptions failed

#149

Earlier quoted context omitted.

Java is the only mainstream language with checked exceptions and checked exceptions are nowhere near usefulness of static typing. Following your logic, Java developers can now say “if you don’t like freedom of Java, use Rust/Haskell/Scala to validate everything at compile time”

Java is not the only language that has checked exceptions. C++ has them too, for example, but thanks god virtually nobody uses them in C++.

Checked exceptions (i.e. "dynamic exception specification") were deprecated in C++11 and removed in C++17.

Re: Why checked exceptions failed

#150
post #140
post #30

Earlier quoted context omitted.

> Catching the root exception is not a bad idea. If you had caught the new exception what would you have done with it? Something relevant to the error condition, probably? There may be some cases where the total set of possible errors is too large to meaningfully handle every one of them specifically (for instance if you call a high level GPU initialization routine that can fail in a myriad of ways) but that's not tr…

Realistically speaking, you should essentially never try to "handle" errors with tricky program logic: propagate them up (automatic in languages with exceptions) and eventually--in as few places as possible--report them to the user so the user can decide what to do, not the code.

I disagree, low level exceptions rarely make sense on their on for the users, the automatic propagation of exceptions just promotes laziness by having a catchall "print(exception); exit(1);" at the top level.

If you're lucky you get a proper stack trace that lets you figure out what's going on, but even that is poor ergonomics.

A high level library should report high level exceptions for instance, not minute details of the lower layers. Nothing is more frustrating that having a program fail and all you have is some cryptic "No such file or directory" or "Operation not permitted" error that doesn't tell you what the code was actually trying to do or give you any hint on how to fix it.

Post reply on HN