Live data from Hacker News

Why checked exceptions failed

borretti.me

271–280 of 318 posts

Re: Why checked exceptions failed

#271

I have extensive experience in C# as well as Java. It is bizarre to suggest that checked exceptions have failed. It is unchecked exceptions that have failed. It is the biggest flaw of C#, in fact. Why? As an example, I wrote some very good C# code, carefully tested it, made it work flawlessly, then suddenly it started crashing. What happened? Someone made a change in a function I was calling, and it started throwing…

> 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…

It may not be recoverable locally, but the caller may know how.

Consider Files.createFile. It throws if the target already exists. That method has no way of knowing what the recovery is, however, the caller might.

It could be that the caller wants to explode, but maybe its part of some kind of singleton launch where the recovery is to pass some piece of data off to whoever created the file originally.

Maybe this is a bad example since there’s not much interesting in the return of Files. But it’s exceptional in that it’s an abort rather than a success.

That said, checked exceptions are still trash because they don’t work across thread boundaries or with futures. ExecutionException is the catch all baked into the design because they needed something. The idea could have been good though because naming error handling visible is useful. Java just has the misfortune of designing early before the kinks had been worked out and now we get nice stuff like Rust has.

Re: Why checked exceptions failed

#272

Earlier quoted context omitted.

How is this different in practice? Checked exceptions are simply "syntactic sugar" for error types (exception objects with a few special handlers to return them and receive them). You can write almost exactly the same code with your Result by ignoring the error until the point you would catch it with exceptions. The only difference is that exceptions bubble up if unhandled, which requires a generic catch to match cod…

> How is this different in practice? It is quite a bit more explicit in that something can have an error and in what ways it can error (if you enrich the type in that way), while still being ergonomical. And it reserves exceptions for the truly unexpected/exceptional cases.

Are we comparing the same things? Adding exceptions to the method signature is pretty explicit to me too.

It's also pretty ergonomical depending on how you generally treat errors you don't care about.

I do find your point about exceptions being for unexpected things interesting: basically, split errors into two classes and use separate mechanisms for each.

I generally feel that this differentiation would never hold between different codebases (an HTTP library may consider network error something "normal", but users of that library might consider it exceptional), but it's surely an interesting concept.

I also think that we as developers are pretty bad at handling both expected and unexpected errors, so having two mechanisms will only make us less likely to do so (not by much, though).

FWIW, my take is that they are largely equivalent except for more or less syntactic support for the approach.

Re: Why checked exceptions failed

#273

Earlier quoted context omitted.

In a desktop application that is not allowed to totally crash, or at least has to crash kind of gracefully, checked exceptions are useful. But in the world that most Java devs live in, which is various flavors of RPC server, failing requests is fine. If lots of requests fail, your monitoring infra should page someone, and that someone will go log spelunking and figure out what's broken. Very occasionally it turns out…

> you should not be defensively programming against all those possibilities This is where libraries and frameworks come into play - they defensively program against that for you. And wrap it all up in a simple interface with, well, checked exceptions.

If I need to write some bytes to an S3 bucket but the network is hosed, there's literally nothing useful I or any library can do until the network is back up.

RPC calls will fail, error logs should get written, and a monitor should get triggered, and someone should get paged so they can wake up and figure out why the network is hosed.

Nowhere in there is it useful for me to wrap all my S3 writes in try/catch blocks.

Re: Why checked exceptions failed

#274

Earlier quoted context omitted.

The problem is that if some code you call throws a checked exception (InterruptedException being an extremely common culprit) then you must wrap... and suddently nobody calling YOUR code can catch that InterruptedException reliably because it's now a SomeException (doesn't even have to be RuntimeException specifically) with an added "suppressed" exception that you now have to check for. ... so the basic "catch" synta…

It's possible to use a type parameter in a throws clause in Java, last I checked (as I also mentioned in more detail above). But it doesn't seem to be common practice, so interop is still a disaster.

That only works up to one checked exception type, because Java doesn't have anonymous union types.

It really is a lose-lose unless Java grows a more powerful system around this.

Re: Why checked exceptions failed

#275

Earlier quoted context omitted.

> you should not be defensively programming against all those possibilities This is where libraries and frameworks come into play - they defensively program against that for you. And wrap it all up in a simple interface with, well, checked exceptions.

If I need to write some bytes to an S3 bucket but the network is hosed, there's literally nothing useful I or any library can do until the network is back up. RPC calls will fail, error logs should get written, and a monitor should get triggered, and someone should get paged so they can wake up and figure out why the network is hosed. Nowhere in there is it useful for me to wrap all my S3 writes in try/catch blocks.

Error logs and monitor needs to known the operation failed somehow, try/catch or return value.

> literally nothing useful I can do

For some services, for others queuing the operation for retry is a thing.

Re: Why checked exceptions failed

#276

Earlier quoted context omitted.

I think there are idioms where your unchecked exceptions can be caught, like via some scoped context, like an actor.

Yes, that's intentional. They are still exceptions because there are rare cases where you want to catch them. They're unchecked because it's not worth the hassle of declaring them everywhere. Python's KeyboardInterrupt is another great example -- it can theoretically happen almost everywhere, but most scripts don't have anything sensible to do with it and it will never happen in a daemon or GUI program. If you happen…

I'm not sure interrupt is the right idiom there either. An actor or coroutine dedicated to processing keyboard input seems more sensible, precisely because interrupts can happen almost anywhere, and non-determinism is not something you want to introduce accidentally or implicitly.

Re: Why checked exceptions failed

#277

Earlier quoted context omitted.

> How is this different in practice? It is quite a bit more explicit in that something can have an error and in what ways it can error (if you enrich the type in that way), while still being ergonomical. And it reserves exceptions for the truly unexpected/exceptional cases.

Are we comparing the same things? Adding exceptions to the method signature is pretty explicit to me too. It's also pretty ergonomical depending on how you generally treat errors you don't care about. I do find your point about exceptions being for unexpected things interesting: basically, split errors into two classes and use separate mechanisms for each. I generally feel that this differentiation would never hold b…

> FWIW, my take is that they are largely equivalent except for more or less syntactic support for the approach.

Yeah, as with lambda calculus and turing machines, they are equivalent in capacity.

> Are we comparing the same things? Adding exceptions to the method signature is pretty explicit to me too.

Maybe, maybe not. IME the IDE support for exceptions is quite a bit worse than for returning results and such.

> I do find your point about exceptions being for unexpected things interesting: basically, split errors into two classes and use separate mechanisms for each.

> I generally feel that this differentiation would never hold between different codebases (an HTTP library may consider network error something "normal", but users of that library might consider it exceptional), but it's surely an interesting concept.

Isn't the two ways to handle it already a thing with checked and unchecked exceptions, though? As in having wrappers that convert one to the other.

> I also think that we as developers are pretty bad at handling both expected and unexpected errors, so having two mechanisms will only make us less likely to do so (not by much, though).

I don't quite agree with that, as with encoding it in the type system conveys more intentionality than with exceptions, IMO.

Re: Why checked exceptions failed

#278

Earlier quoted context omitted.

So if you write code that handles dates you'd throw NotWeekendException to notify the caller that it's not weekend? Exceptions should be for exceptions, not control flow.

I'm curious how this is possible? Exception throwing and handling is fundamentally a flow control construct. ie: throwing an exception must control flow. A counter-example snippet where throwing an exception does not control flow would be appreciated.

He means exceptions are not for common control flow that shows up everywhere, like a return value. You want an exceptional control flow change when your hard drive runs out of storage, but you don't want exceptional control flow change when your hashtable doesn't have the key you're looking for.

The categories and frequency of these conditions are very different.

Re: Why checked exceptions failed

#279
post #200

Earlier quoted context omitted.

This feels argumentative but fine. If you're unsure then make it a runtime exception. I agree that a lot of the problems people have with checked exceptions is that people over use them. You don't want to handle the checked exception wrap it with a runtime exception. But as an author if there's something important, I want to give the user of the library as much help as I can.

But as an author if there's something important You're still ignoring other people's point in this thread: you (the author of a library/package) cannot decide what is important for the users of your library. You can guess, but your guess will always be wrong for a subset of your downstream users. And speaking from the other side, as an application developer: for most of my code, an ArrayIndexOutOfBoundsException is a…

ArrayIndexOutOfBoundsException is a runtime exception...

You're ignoring the fact that I'm not saying EVERYTHING should be a checked exception. Quite the opposite.

Re: Why checked exceptions failed

#280

Earlier quoted context omitted.

What about: NullPointerException? ArrayIndexOutOfBoundsException? IllegalArgumentException? UnsupportedEncodingException?

What about them? If your language semantics are pervaded by exceptions, that kinda suggests your language sucks. If you're unlucky enough to find yourself in this situation, then make exception contracts simple to express and propagate.

They aren't simple to express and propagate?

What's so hard about declaring throws?

Post reply on HN