8 ms·
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
by breadwinner 3y ago
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 a new exception. This would have caused a compile error in Java, not a crash.
The list of recoverable exceptions that can be thrown by a method should be part of the contract. It should be written down by the programmer, and enforced by the compiler. If not, then to avoid crashing you would have to catch the root Exception class, which everyone agrees is a bad idea.
More on checked vs unchecked exceptions here:
https://forum.dlang.org/thread/hxhjcchsulqejwxywfbn@forum.dlang.org https://forum.dlang.org/thread/hxhjcchsulqejwxywfbn@forum.dl...
- osigurdson 3y agoExceptions are great when they don’t need to be caught. Otherwise they are a pain.
- _a_a_a_ 3y agoEach 'leaf' statement of language should have a definite and finite list of exceptions that it can throw. Each non-leaf statement is a composition of leaf statements. Therefore every exception can be determined, and that's our checked exceptions would work; work at the exceptions and check they match the list. Therefore even without checked exceptions the compiler could provide the exceptions you want and you can check them or not if you please, so get the best of both worlds. All without forcing people to give an explicit list of exceptions everywhere. Forcing checked exceptions is not going to be popular, and much of the time is counter-productive. Also someone broke your Liskof Substitution Principle which may be your bigger problem.
- funnymony 3y ago“I wrote some very good C# code“ We all do.. its always that other guy who is at fault :)
- scarface_74 3y agoCatching the root exception is not a bad idea. If you had caught the new exception what would you have done with it? Most of the time, it’s either catch the exception, log it in a central logging system keep moving and have a central alerting system or catch the exception log it and crash the program.
- revskill 3y agoCatching at the root is only half of story. You must also have ability to resume where it was thrown.
- simias 3y ago> 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 true in all cases. Maybe the new error was in fact recoverable, or maybe it calls for some more specific diagnostics. At any rate I completely agree with the parent that changing the set throwable exceptions should be considered a breaking API change and should be enforced by the compiler. If an API method wants to future-proof and be able to throw anything, it can declare just that and everybody will know to expect the unexpected. That's why I vastly prefer Rust's Result<> system which does most of what exceptions do and with fairly similar ergonomics but with normal return values and all the type checking that it involves.
- scarface_74 3y ago> 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.
- simias 3y agoThe 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.
- croes 3y ago>you would have to catch the root Exception class, which everyone agrees is a bad idea. It's a bad idea to catch an unexpected exception?
- simias 3y agoThe issue brought up by the parent is that the exception needed not be unexpected. If the compiler had generated an error because a new exception type could be thrown by the third party API, the developer could have decided that they either couldn't do anything with it and just add a catchall, or actually implement code specifically to handle this issue. IMO the entire concept of "unexpected exception" is bogus in and of itself. That's like calling "exit(1)" in your code when something goes wrong instead of using proper error handling facilities. Admittedly it's very convenient for small scripts where proper error handling might be overkill, but for any serious application it's a massive footgun IMO.
- tom_ 3y agoYou run the risk of swallowing something interesting. This was always the problem with C# exceptions to my mind: there was just one mechanism for reporting errors, which covered everything from the most non-erroneous of events, that aren't even errors, such as file not found or invalid file name encoding or failed to write data to file or socket error during write, to stuff that absolutely should cause the program to go pop and die immediately, such as a null pointer or divide by zero. Anyway, I sympathize with this thread's OP, as I've had exactly the same problem with C#. In the end, I came to the conclusion you kind of do often have to catch every exception, because you can't trust the functions you call, and the documented exception lists for framework stuff are not always accurate. Safest is to put each call in its own try...catch block, and do any property accesses outside the try...catch block so you find out about the NullReferenceExceptions. This is annoyingly verbose though. It also does assume your setters and getters don't throw anything. I mostly liked C#, but the exceptions aspect is not good. Similarly, I've generally despised working with Go, but the way it deals with errors is not its worst feature! (You do have to pay attention to the linter output though! It's the same err variable every time, so the compiler's fastidious checks for variable use are always foiled...)
- EMM_386 3y ago> Someone made a change in a function I was calling, and it started throwing a new exception. If it has nothing to do with your code, why not either let it bubble up the stack or try/finally it to do whatever clean-up you need to do before re-throwing it and letting it move on to a global logger or similar. If it is something you care about you can always catch the specific exception type and handle it. Maybe whoever worked on the other function wasn't even aware it might throw that particular exception. Granted I am far more experienced in C# than Java, but the idea that with every code change you potentially have to review and declare every single type of exception seems like madness ("what happens if we pull the cord when it's here, what type will that throw?").
- breadwinner 3y agoImagine 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.
- Akronymus 3y agoThis 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<'t,string> 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.
- necovek 3y agoHow 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.
- beached_whale 3y agoWhy not catch System.Exception also? Or just catch
- breadwinner 3y agoWhen 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 fix your code by removing the catch-all.
- beached_whale 3y agoI meant at the higher layers, minus some potential cleaning, a lot of library code is exception neutral and shouldn’t care
- Kwpolska 3y agoCatch 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.
- breadwinner 3y agoIf the exception that was thrown (by a method you called) is in fact a condition your code can recover from, then the functionality of your code was needlessly aborted.
- wredue 3y agoThe “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
- Kwpolska 3y ago> 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 avoid crashing the entire app is a good idea. Java also has RuntimeException, and with it, a whole host of exceptions that may occur but are not declared. Should every function that uses the division operator be marked with `throws ArithmeticException` since it may end up being a division by zero? That would quickly become absurd.
- arzig 3y agoIt 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.
- kaba0 3y agoYes, 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.
- ahtihn 3y agoHow are you supposed to recover from IllegalArgumentException or NullPointerException?
- dbrueck 3y agoI stopped using Java a long time ago, and so I assume the language has gotten better since then, but early on at least it felt like Java almost took pride in making the developer jump through extra hoops. Compared to many other languages, using Java just made me feel tired. Checked exceptions - a feature that seems to be a cost to the developer 100% of the time while being a benefit far less than 1% of the time - is a quintessential example of Java's tendency to cause developer fatigue.
- naasking 3y ago> Checked exceptions - a feature that seems to be a cost to the developer 100% of the time That's because exceptions are overused for not-truly-exceptional conditions. Languages that throw exceptions when a dictionary does not have a key you're looking for are doing it wrong.
- narag 3y agoJava almost took pride in making the developer jump through extra hoops That's exactly it. A holier-than-thou stance, reprised by most comments in this post. Checked exceptions were a PITA. Someone has recommended to use another language if you didn't like it. Unfortunately my employer at that time had entered a partnership with Sun that prevented that :) Another ideological prohibition: no pointers. Well, how do you assign event handlers then? People were extremely confused with anonymous classes. I got the reason instantly.
- breadwinner 3y agoSome 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.
- dbrueck 3y ago> The point is, if you don't need the rigor of a strongly typed compiled language This has nothing to do with strongly typed compiled languages vs alternatives.
- Kamq 3y ago> Someone made a change in a function I was calling, and it started throwing a new exception. This would have caused a compile error in Java, not a crash. I call BS here. I've worked in a few Java projects, and in every single one, the people changing the method in question would have thrown RuntimeException to stop the compile errors. If RuntimeException was checked, you might have a point, but given that there's a class of unchecked exceptions, you're relying on discipline instead of the compiler anyways.
- breadwinner 3y agoYou 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.
- Kamq 3y agoI will acknowledge that this wasn't the best code (or the best programmers). I will also cede the point that you can write good code in Java. But my point was that Java wouldn't have enforced the situation to be a compile error, not that you can't write good code in Java. I maintain this point, and I think it's true for the average java project. If you're getting these benefits from Java, it's the intersection of Java and your team's or your organization's culture, not purely from Java. It's possible for you to gain this benefit and for checked exceptions to have still failed out in the wider world of the average Java project.
- xen0 3y agoAnd 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.
- breadwinner 3y ago> 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.
- crabbone 3y agoThis is very simplistic view of the problem. This completely glosses over modularity, ABI, performance optimizations... just to name a few. How are you going to write generic functions that take functions as arguments and re-throw the errors thrown by these functions, if you use checked exceptions? Will you require that the acceptable functions only throw exceptions that you like? -- Then your generic function is close to being worthless... If exceptions are encoded in function's interface, then they have to be in ABI, but then you must have non-trivial types available when marshalling data between two components, so, you cannot serialize the communication using some protocol with a fixed number of types (eg. JSON and friends), because now you need to account for the infinite variety of exception types. Because of at least these two things, what I saw happen a lot of Java / C++ projects (god blessed me with very little C# exposure) was that as soon as a developer encountered a function with checked exceptions, a wrapper was written which changed the type into a runtime exception. This is so because exceptions are supposed to be handled separately, and often the author of the function has no idea how they need to be handled -- so they want to concentrate on the main goal of the function. Once functions grow into garlands of try-catch-catch-catch-...catch the focus is lost. It becomes very hard to understand why the function was written in the first place, because the error handling takes over every other concern.
- interlocutor 3y ago> 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.
- newaccount74 3y agoIt'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.
- 3y ago