Live data from Hacker News

Why checked exceptions failed

borretti.me

181–190 of 318 posts

Re: Why checked exceptions failed

#181

Earlier quoted context omitted.

Some people say the same about strong typing. Like, why do I have to write down the type of every single parameter or variable? Java is making me jump through hoops! The point is, if you don't need the rigor of a strongly typed compiled language, there are other languages you can use. Perhaps a bash script is all you need.

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…

F# is strongly typed as well, and I almost never have to write type annotations. Hindley milner type inference is pretty useful. And my IDE of choice also shows the inferred types for easy checking. Altough, without that, it'd be a PITA, as you said about haskell.

Re: Why checked exceptions failed

#182

Earlier quoted context omitted.

> write generic functions that take functions as arguments and re-throw the errors thrown by these functions There is a philosophy that applies here: simple things should be simple, complex things should be possible. The scenario you're mentioning is not common enough that the language design should be centered around it.

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.

In the case of map function hopefully you're using it with methods that don't fail in serious ways, and don't need strong error recovery. If so Java has RuntimeException to handle that case. If serious errors are possible and strong error recovery is needed, then you need to avoid the conveniences offered by functional style programming.

Re: Why checked exceptions failed

#183
post #146

Earlier quoted context omitted.

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

A parser failing is something I'd expect to be recoverable. And I'd expect it to return where it failed and why. You can do that without throwing exceptions at all, and instead encode it in the type. Like returning a result. (Can be further refined, like adding the already parsed/yet to be parsed string to the failurereason as a value) I WANT to be forced to handle it right there and then rather than potentially forgetting a try catch, or checking the boolean returned if it was successful or checking the out parameter if it is not null (boolean tryparse(out parsedObject) is something I see commonly)

For me it is much more ergonomic to use a

  match tryparse somestring with
  | Ok obj -> proceedFurther obj
  | Error reasons -> handleerror reasons
For truly exceptional cases you don't expect to be recoverable? Yeah, throw an exception.

Re: Why checked exceptions failed

#184

Earlier quoted context omitted.

This comes from a misunderstanding of the reason why exceptions where designed they way they were. The whole point of exceptions bubbling up w/o having to write support code to deal with passing exceptions further is to make it so that the purpose of the function is clear to the reader. Go's exceptions have the same unfortunate property as Java's checked exception. And that's what makes Go's code atrocious. Every oth…

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.

Re: Why checked exceptions failed

#185

Earlier quoted context omitted.

Imagine that the functionality implemented by your method is very important. This functionality should not be needlessly aborted. If so you want to be aware of errors you can recover from, correct? This is why checked exceptions are helpful. It gives you a guaranteed-by-compiler list of exceptions and you can decide which of those you should recover from.

This is one of the main reasons why I personally try to avoid exceptions and instead encode failure into the type system. For example, returning a Result rather than just a 't. (You can refine the type further). Altough, in most languages that approach results in a lot of boilerplate. In F# it works quite well, though.

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 code ignoring errors otherwise.

Re: Why checked exceptions failed

#186

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.

What about: NullPointerException? ArrayIndexOutOfBoundsException? IllegalArgumentException? UnsupportedEncodingException?

Re: Why checked exceptions failed

#187
post #55

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…

Yes, in Java’s parlance an Exception is a recoverable code path, Error is the kind that is not. E.g. a network call failing due to some IOException can be easily retried, that’s a proper error handling.

How are you supposed to recover from IllegalArgumentException or NullPointerException?

Re: Why checked exceptions failed

#188

Earlier quoted context omitted.

Any chance you have a code/pseudo code example of this? My naive take on this would be “just use raii/closable”. (But easily and likely, I misunderstood)

Let's say Java had no checked exceptions. I could just write IO code without doing a try since no one is forcing me to check anything. But let's go further. Let's say I have an API x(). This API accesses my database as part of a larger transaction. I invoke x() and it fails with an SQLException which is declared. I can revert the transaction or I can choose to retry x(). The checked exception notifies me that there's…

On its own, seeing ‘SQLException‘ in method signature, does not provide you with information: “this is part of bigger transaction, and you have ability and responsibility to retry or rollback”.

That will be part of lib documentation, which should be read & understood regardless of existence of checked exception.

Re: Why checked exceptions failed

#189

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.

In the case of map function hopefully you're using it with methods that don't fail in serious ways, and don't need strong error recovery. If so Java has RuntimeException to handle that case. If serious errors are possible and strong error recovery is needed, then you need to avoid the conveniences offered by functional style programming.

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" syntax starts to fall apart because now you have to catch everything and resort to stuff like Guava's Throwables helpers.

It's madness.

The problem ultimately is variance: Methods are covariant, but throws clauses must be contravariant.

There are ways to solve this but "checked exceptions" (as in Java) are not the right way. Ask anyone who's worked in Scala on the JVM which they prefer and you'll have your answer.

Re: Why checked exceptions failed

#190
post #36

Earlier quoted context omitted.

> Let's say that the creator of the interface allows IOException, but the backend of my implementation is a database so I have SQLExceptions, same issue. The creator of the interface should decide on a generic exception type: public interface UniversalStorageInterface { void store() throws StorageException; } Then, in the present, the FileStorage can define (and throw) a FileStorageException (inherits from StorageExc…

So now whoever creates the interface needs to define an exception type per method, and whoever implements the interface needs to define a subclass of that per method and catch-and-wrap every exception their callee raises. You better add some serious tooling built into the language to facilitate this, because from experience ain't no way anyone's going to bother with this if they have to handroll it, even with IDE cod…

Note that StorageException in the previous example could also simply be a BaseStorageException that all the methods throw, even if one of them is DiskFullError(BaseStorageException) or ObjectStorageLimitExceededError(BaseStorageException), and another is InvalidFilenameError(BaseStorageException).
Post reply on HN