Live data from Hacker News

Why checked exceptions failed

borretti.me

101–110 of 318 posts

Re: Why checked exceptions failed

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

> Something relevant to the error condition, probably? And in every language I know you can have catch blocks for specific errors and then have a generic catch all.

The ability to catch exceptions is not the issue, it's knowing what to do once they're caught that's the problem.

The parent apparently had exhaustive exception handling, catching all cases. A new exception is now generated, probably signaling a new error condition, you can't expect the calling code to be able to handle it gracefully.

Hence why a compile error might be a better solution here, the coder could decide whether the new exception can fit an existing handler or requires special handling.

Re: Why checked exceptions failed

#103
Yeah. The thing is, I like the idea of checked exceptions. You could go ahead and define a number of error situations or error kinds and annotate methods with these checked exceptions to force applications to handle these errors. Like, you have a Database.query function and it might throw different exceptions - ConnectionInterrupted, QueryPreparationFailed, QueryFailed, TransactionCancelled. And then you could catch specific error kinds and react differently on those - retry for interrupted connections, cancel on query errors.

This however runs into issues. One really annoying one: Checked exceptions are part of your API. If you forget an error condition and want to introduce a new exception for that, well guess what, that's a breaking API change. There is no easy way to evolve an API with checked exceptions, because changing them requires new major versions, because it prevents code from compiling.

On top, if you want to encapsulate your API properly, you end up with a lot of boilerplate. Once you're using a library, and that library has checked exceptions, you either have to base your own public API on the API of that library - meaning you can never replace it - or you have to start wrapping all manner of errors into your own checked exceptions. I've kinda done it as an experiment some time ago, but that just ends up with so many exception types and so much error wrapping it's kinda ridiculous.

And then you end up with the sad truth on top to be honest: Most error checking is rather brute and clumsy. In most cases, I just let exceptions bubble up because my intermediate function can't really do anything about it. As a distant second, you catch all errors, shove them in some kind of error reporting, reset the system and continue trucking. As a somewhat similar third, I dissect errors in CLI tools to create some useful error messages. And only them I might start caring about some specific errors, but that's pretty rare if you're just running some REST-based business logic.

All in all, it's a good idea, but the implementation results in a lot of API churn or boilerplate for something that's not used much in general.

Re: Why checked exceptions failed

#104

My opinion is the exact opposite see https://debugagent.com/everything-bad-in-java-is-good-for-yo... Checked exceptions are unpopular since no one likes responsibility. But they are great when used right. Calls that must have proper cleanup after them e.g. SQL, IO are checked. The fact that this must be communicated via interfaces is hugely important. There are "weird" problems such as stream close() throwing a check…

From your OC:

> Null is fast. Super fast. Literally free.

Yes and: The JIT will optimize away Null Object method invocations.

All my composable classes have a Null Object. No null checks means concise iteration (eg graph traversal). Eliminates NPEs. Just as fast.

Win / win / win.

Optionals (classes and operators) continue to be turrible mistakes. So much unnecessary effort, so little benefit.

Ditto @Nullable and @Nonnull.

Someday, maybe, I'll make a javac compiler plugin which autogenerates Null Object implementations and converts "Node abc = null" to "Node abc = Node.DEFAULT_NULL_OBJECT_INSTANCE".

https://en.wikipedia.org/wiki/Null_object_pattern

Re: Why checked exceptions failed

#105

Earlier quoted context omitted.

> Whatever the “root” of your code is should have a generic catch all where you do “something” If your only goal is to avoid crashing the app then you can catch the base Exception class at the root of you code and go home. But consider the scenario that you're writing a very important method, where the functionality of your method is very important. If your method does not do its job, the rocket may crash or the pati…

And then you have a list of catch blocks with the final one being the base.

No. You only catch exceptions you can recover from in your method. Other exceptions should be handled in higher layers.

Re: Why checked exceptions failed

#106
post #29

Little aside, but I feel a lot of the drama surrounding exception could have been solved with a little syntactic sugar making their handling easier. Something along the lines of Perl's "|| die("...")" pattern would be a start (i.e. add some context and rethrow). In C++ I find it quite infuriating that try{} opens up a new lexical scope, which means you can't construct something, check for errors and move on, since th…

> infuriating that try{} opens up a new lexical scope This slays me. The try, catch, and finally blocks should be just one lexical scope.

I don't think that helps?

If, as is kind of the point of RAII, your constructors do some initialization and that initialization may fail, then:

If you have a single object that may fail to construct then maintaining it in scope for the catch block doesn't make sense; it's not initialized.

If you have several, then you still can't maintain them in scope, because you don't know which ones are actually valid.

Yes there are other patterns that wouldn't have this issue (acquiring resources outside of the constructor), but then you can just declare your objects outside of the try-catch block.

Re: Why checked exceptions failed

#107
post #99

Earlier quoted context omitted.

You need to help train your coworkers to write better code :) Point this thread out to them. Seriously, Java doesn't force bad programmers to write good code. It just enables good programmers to write good code.

And a lack of checked exceptions means good programmers can't write good code? So good code may only be written in Java? No... I don't think so. Checked Exceptions are at best, just a 'hey, are you sure that's right thing to do here?' and at worst, an additional source of rigidity in your code that blows up the scale of a minor change.

> And a lack of checked exceptions means good programmers can't write good code?

Right, and that was my point at the top post in this thread.

Re: Why checked exceptions failed

#108

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…

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 digit and it encounters an alphabetic character so it throws an exception!

Re: Why checked exceptions failed

#109

Earlier quoted context omitted.

When you catch the root Exception class in C# you end up catching IndexOutOfRangeException as well. You should let the program crash instead because this happened due to a bug in your program. Continuing as if nothing happened is unsafe. Another issue is that you are not allowing higher layers see the exception, even though they may have logic for recovering from the exception. To see the exception they have to now f…

Catch the root Exception class, show an error, but let the user continue working if possible. For a web app, there already is a catch-all exception handler at the request level that prevents the entire server from crashing. For other environments, having a catch-all handler at some top-level interaction point (i.e. on buttons or menu items) would be a good choice too.

The “if it’s possible” is a major part of the reason to not catch the root exception.

Catching typed exceptions give you much more easily parsable details about whether or not “it’s possible” to recover.

It’s bad practice to catch the root exception. More often than not, it points to an inexperienced developer

Re: Why checked exceptions failed

#110

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.

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”

Checked exceptions are part and parcel of a type system, though. If you have a foo function that returns a value or error you've got different options. Pseudo code they are:

foo():boolean throws Some, List, Of, Errors

foo():boolean | Some | List | Of | Errors

foo():(boolean | nil) , (nil | Some | List | Of | Errors)

The first is checked exceptions. The second is returning different types. Typically there's some sort of Option wrapper for ergonomics. The third returns a result, err tuple and by checking if err is nil you can see if foo succeeded.

Ultimately they are all the same. What differs is the boilerplate/syntax to accomplish what you want. If what you want is a generic error to handle unknown events then you gotta write it that way no matter which system you use.

Post reply on HN