Live data from Hacker News

Why checked exceptions failed

borretti.me

241–250 of 318 posts

Re: Why checked exceptions failed

#241
post #159

Earlier quoted context omitted.

You are very confused... "strong" in strong typing doesn't mean you have to write much or at all. Actually, it doesn't mean anything really. But, let's say, Haskell is probably at least as "strongly typed" as Java -- at least that's how most people understand that wording. And you don't have to write types in Haskell at all. It will be a nightmare (as if Haskell can be anything else, but even by the very low Haskell…

> Until very recently, Java was just tedious, repetitive, high-entropy language, where you had to write everything multiple times. More than half a decade ago.

Which means today's code.

Re: Why checked exceptions failed

#242

Checked exceptions failed because 99 times out of 100 the exception is not recoverable, so the try/catch block is just wasting everyone's time. (In 7 years as a Java dev I can think of one time I wrote code that tried to recover from IOException instead of just making the caller retry.) Even when the exceptions is theoretically recoverable, it has to get propagated up properly to the caller who should be handling rec…

I find the rust `?` construct nice for this: give me the success result or propagate the error. It is based on a result type though.

The try/catch construct require too much code for the common propagate case.

It would be nice if Java had a similar construct for error handling.

Re: Why checked exceptions failed

#243
post #239

Earlier quoted context omitted.

It's a very common pattern, almost every modern language has a "map" function. The map function can throw a superset of the exceptions that its argument can throw. If checked exceptions can't deal with this, they'll be of limited use.

> The map function can throw a superset of the exceptions that its argument can throw. Wait. Why should a map care about what exceptions it's arguments can throw? A map is storing a thingit. A thingit should exist independently before it gets placed into a map. Placing a thingit into a map should not invoke anything on the thingit. The only exceptions coming back from attempting to place a thingit into a map should b…

You're thinking of a finite map, also called a hash table or a dictionary.

The map function takes a function and a list and applies the function to every element of the list:

  map(double,[1,2,3]) = [2,4,6]
Grandparent could've used the for loop rather than the map function to make his point:

  for i in [1,2,3]:
      print i * 2
-- because in general instead of the i * 2 we might have a call to a function that might raise an exception.

ADDED. That is wrong: the for loop would not a good example at all because it is not customary to declare the type of a for loop or to need to declare which exceptions a `for` loop might throw.

Re: Why checked exceptions failed

#244

Checked exceptions failed because 99 times out of 100 the exception is not recoverable, so the try/catch block is just wasting everyone's time. (In 7 years as a Java dev I can think of one time I wrote code that tried to recover from IOException instead of just making the caller retry.) Even when the exceptions is theoretically recoverable, it has to get propagated up properly to the caller who should be handling rec…

Is this a problem with checked exceptions or a problem with Java? Couldn't exceptions be treated as robustly as param and return types in the generic system so they are more composable? Why not a Future interface?

The argument that some exceptions will always be runtime (like oom) so checked exceptions are flawed is harder to argue against but I would also say it's a matter of opinion whether you feel like it's worth dropping entirely.

Re: Why checked exceptions failed

#245
Erlang does this the right way: the process fails (the Erlang sub-process) but the super visor tree anticipates such failures and will restart the process that failed which if the rest of the application is properly designed will handle the error without loss of consistency. Depending on the failure it can also escalate further up to deal with larger and larger levels of malfunctions. Properly implemented short of gross hardware failure (all nodes down) this will result in a system that is always at or near the maximum possible availability as permitted by the hardware.

Re: Why checked exceptions failed

#246

Earlier quoted context omitted.

No. This comes from a misunderstanding of checked exceptions. Checked exceptions don't mean I need to handle the exception right now. They mean I need to either do that or declare throws. Declaring throws is fine it implicitly documents the code and enforces a similar requirement up the chain. Checked exceptions aren't the default and shouldn't be.

Checked exceptions should be the default, exceptions should just be rare. They are overused in almost all languages.

The fundamental thing an exception does is allow you to choose where in the stack to react to the exception. The fundamental thing a checked error does is force you to actually choose (even if that choice is to bubble all the way up and crash the program), rather than forget and end up with a bug.

The choice about where to handle the exception is almost always a good thing, since it reduces boilerplate in dealing with that condition. The cases where it's not a good thing are when it's either impossible to deal with the condition at all (e.g. assertion failure) or where it must obviously be handled locally (e.g. errno==EAGAIN type stuff).

The requirement to actually make the decision is a good thing in some cases (e.g. I/O), but not in other cases (e.g. out of memory) because the overhead of making this decision so often is larger than the benefit of avoiding that class of bug. It's beneficial when it's something that can only happen while doing certain well defined activities.

There are also places where there just shouldn't be an error at all. E.g. null pointers shouldn't be an error; your program should be rejected if the compiler can't prove that this is impossible through static analysis (even if that just forces the author to write assert(ptr!=0)).

I guess I'm converging on a scheme that looks like: I/O errors are checked exceptions. Out of memory is an unchecked exception. Assertion failures are panics which cannot be caught. Division by zero is prevented by static analysis. I'm not sure what should be done with EAGAIN/EWOULDBLOCK type stuff -- it doesn't really feel like those should be exceptions, but introducing a whole new "error" handling idiom just for this doesn't feel great either.

Re: Why checked exceptions failed

#247

Checked exceptions failed because 99 times out of 100 the exception is not recoverable, so the try/catch block is just wasting everyone's time. (In 7 years as a Java dev I can think of one time I wrote code that tried to recover from IOException instead of just making the caller retry.) Even when the exceptions is theoretically recoverable, it has to get propagated up properly to the caller who should be handling rec…

I find the rust `?` construct nice for this: give me the success result or propagate the error. It is based on a result type though. The try/catch construct require too much code for the common propagate case. It would be nice if Java had a similar construct for error handling.

The Task type in C# with the await unwrap sugar is similar to this. You can even check for and grab the exception without throwing if you want to.

Still, it's too bad the error type must be a throwable. I kind of wish it could just be a plain type so you can error or cancel without generating stack traces. Awaiting a failed task could still throw.

Would be a nice perf boost. As it is now, you don't want to actually cancel or fail a C# task in performance critical code. You need to successfully complete the Task and return an error, which is pretty confusing.

Re: Why checked exceptions failed

#248

Checked exceptions failed because 99 times out of 100 the exception is not recoverable, so the try/catch block is just wasting everyone's time. (In 7 years as a Java dev I can think of one time I wrote code that tried to recover from IOException instead of just making the caller retry.) Even when the exceptions is theoretically recoverable, it has to get propagated up properly to the caller who should be handling rec…

I find the rust `?` construct nice for this: give me the success result or propagate the error. It is based on a result type though. The try/catch construct require too much code for the common propagate case. It would be nice if Java had a similar construct for error handling.

"throws IOException" is too much code? Or is the issue more that you can't really do autocoercion to a declared thrown type in Java the same way that you can do in Rust?

Proliferation of types is an issue in Java, but the whole language has that problem. It's not just exceptions.

Re: Why checked exceptions failed

#249
post #197

Earlier quoted context omitted.

> important failure that can't be dismissed But importance of the failure is determined completely by the program, not the library. Grep fails to open a file for reading -> message the user and exit Nuclear reactor controller fails to read important a file -> initiate reactor shutdown or something. If file read is critical, you have to handle failure no matter what the interface is. Because you know that disk can fai…

Grep fails to open a file for reading -> message the user and exit That should be: message the user and process the next file

:)

Re: Why checked exceptions failed

#250

Checked exceptions failed because 99 times out of 100 the exception is not recoverable, so the try/catch block is just wasting everyone's time. (In 7 years as a Java dev I can think of one time I wrote code that tried to recover from IOException instead of just making the caller retry.) Even when the exceptions is theoretically recoverable, it has to get propagated up properly to the caller who should be handling rec…

I always feel like the error domain changes as you go across program domains.

I keep returning to something about error handling that bothers me. People argue what the proper way to handle errors is without really considering that it's highly context sensitive. Which makes me think you should be able to pass an error handler down to lower level functions that tells them what to do when something bad happens. Sort of like recent ideas where you pass functions a allocator instead of them calling malloc() directly or whatever.

Post reply on HN