Live data from Hacker News

Why checked exceptions failed

borretti.me

251–260 of 318 posts

Re: Why checked exceptions failed

#251

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.

Declaring throws end up leaking layers of abstractions in practice. Many thrown checked exceptions are not appropriate to pass above certain layers. It is often not OK to expose checked exceptions that are really implementation details up the chain—-it’s not a “similar requirement”—-it is exactly the same requirement and often an inappropriate one at many levels up the chain. If you just pass exceptions up in throws,…

You make a great point about exposing the exceptions of library code. Why _don't_ linters deal with this better? It seems like you could throw up a yellow squiggly on any public method that throws an exception outside of its package or some such thing.

I agree that it takes a lot of discipline to do this properly. Is that discipline alleviated with unchecked exceptions or would you say that doing things right takes the same work, checked or unchecked?

Re: Why checked exceptions failed

#252
post #18

While I broadly agree with this, the author doesn't appear to go far enough himself. > Functional error handling, using Option and Result types, is rapidly becoming the standard operating procedure in essentially every language, because it relies on nothing but values and types. They are more of the same, and so they fit right into the existing language machinery. I agree that Optional types are better than checked e…

The problem with union types for exceptions is that it becomes impossible to return an error as the correct case, or at least there is no way to discriminate any more. Functions that deal with errors also need to be able to have exceptional situations, and in many cases there are completely generic functions as well that can error out for generic reasons. A basic example is the elementary case of having a list of errors for some reason and indexing out of bounds producing an error, what if one have a list of values, which are all derived from itself accessing lists, each of which contains “Ok:Val+Err:OutOfBounds”, accessing that list itself now becomes indeterminate.

The other issue is that the language needs to have support for union types rather than merely sum types. And there's a good reason almost no statically typed language has support for union types since it removes provability and it's not something that can in easily be statically decided, requiring dynamic type tags, but then again, that's what sum types effectively are as well.

Typed Racket is statically typed and has union types, but it's an extra layer on top of a dynamically typed language, so the type tags where there already, but it's not easy to erase type data at run time not to implement.

In your example of “String|Error”, it essentially requires that every String is now tagged at runtime with tag information about it in practice.

Re: Why checked exceptions failed

#253

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 don't think they are meant to be recoverable from, but instead they are a way to provide a controlled shut down of an irrecoverable failure, or to limit the blast radius of a localized failure.

In that sense, they are quite useful. Saving the file generated an exception - instead of suddenly closing the application, display an error. Or maybe a database is not accessible anymore. Instead of suddenly ending the service, trigger a controlled shutdown (log, send alerts, etc).

I don't think anyone would expect exceptions to propagate through RPC calls. A call fails, probably containing a description of the fail. Why should it propagate?

There are ways to handle errors and return errored RPCs without exceptions, such as internal error codes. But error codes are just as irrecoverable as exceptions.

Re: Why checked exceptions failed

#254

Earlier quoted context omitted.

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.

Personally if I were to argue why rust error handling is better than Java it wouldn’t be the too much code part.

I feel the same way about Go vs Java or Zig vs Java, I think errors as values makes more sense.

It’s the laws of the universe breaking and control flow changing on errors that I think most people hate.

Re: Why checked exceptions failed

#255

Earlier quoted context omitted.

This is kind of a ridiculous thing to worry or think about. I’m seeing a lot of type inference in modern Java, and it’s the default way of writing Kotlin.

It is dangerous to call a method on an object whose type is not being checked by the compiler. Whether that's a ridiculous thing to worry about depends on how important your program is. If you work for NASA and your code will run inside the Mars rover, it is not ridiculous to worry about such things, but if you're writing some code that will be used once and thrown away then yeah, it might be ridiculous.

It is being checked by the compiler though. For your example to work both would need to extend a common object (unless the only thing you ever do to that object is call fire(), which is nonsensical) or you would get a compile time error right off the bat.

It's a ridiculous thing to worry about in either of your cases. That incredibly specific situation simply isn't ever going to happen in practice because so many other conditions need to be met for the compiler to let you proceed.

Re: Why checked exceptions failed

#256

Earlier quoted context omitted.

someMethod() could return an implementation of Employee that overloads the fire method to dispatch missiles. The point being, someMethod is doing some job that you want it to do. It is incredibly unlikely to simultaneously start doing a completely different job, while also returning something of the same shape.

> an implementation of Employee that overloads the fire method to dispatch missiles If you intentionally break it, that's on you. > It is incredibly unlikely to simultaneously start doing a completely different job, while also returning something of the same shape. But it doesn't need to return something of the same shape! The shape is not being checked. All the compiler checks is for the existence of a single method…

[deleted]

Re: Why checked exceptions failed

#257

Earlier quoted context omitted.

someMethod() could return an implementation of Employee that overloads the fire method to dispatch missiles. The point being, someMethod is doing some job that you want it to do. It is incredibly unlikely to simultaneously start doing a completely different job, while also returning something of the same shape.

> an implementation of Employee that overloads the fire method to dispatch missiles If you intentionally break it, that's on you. > It is incredibly unlikely to simultaneously start doing a completely different job, while also returning something of the same shape. But it doesn't need to return something of the same shape! The shape is not being checked. All the compiler checks is for the existence of a single method…

> > an implementation of Employee that overloads the fire method to dispatch missiles

> If you intentionally break it, that's on you.

The shape is not being checked. All the compiler checks is for the existence of a single class of the same name. You call that type checking?

Re: Why checked exceptions failed

#259
post #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…

You can return union types in Java if you want that, although it'd be nice to have better first class support for them.

The issue though is that forcing people to handle irrecoverable exceptions is just kind of dumb. If I do file IO, I know it'll barf sometimes. Sometimes I care, but the vast majority of the time I don't.

A lot of Java servers have RPC endpoints that basically say "write X to file Y" or "read X from file Y" and if the S3 connection is busted, there's nothing the server can do about that. Someone has to go log spelunking, figure out what's wrong (network is misconfigured, AWS is having an outage, whatever) and fix it. So it is bad API design on Java's part to make every single one of those RPC endpoints wrap every single thing that does file IO in a try/catch.

Re: Why checked exceptions failed

#260

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.

Yeah, that'd be nice.
Post reply on HN