Summary:
Anders Hejlsberg, the lead C# architect, talks with Bruce Eckel and Bill Venners about versionability and scalability issues with checked exceptions.
The ability to add new comments in this discussion is temporarily disabled.
Most recent reply: May 6, 2013 11:03 AM by
|
Anders Hejlsberg wrote: C# is basically silent on the checked exceptions issue. Once a better solution is knownand trust me we continue to think about itwe can go back and actually put something in place.
This proves to me that this guy is smart and cares about what he's doing. C++ exceptions were an experiment, and Java checked exceptions were an experiment too. Why do we want experimental features in a programming language? Let's have well-defined, thought-out features in our languages; they are not some designers' playground.
I think that proper (and improper) handling of exceptions will continue to be an issue until we get transactional semantics in our programming languages. How else do you guarantee that you recover properly when an exception is thrown? The problem is too hard to handle correctly otherwise.
|
|
|
> How about having "unexpects" keyword ?
It is already there, but it's called "//unexpects". ;-)
|
|
|
> It is already there, but it's called "//unexpects". > ;-)
Having "unexpects" as a keyword make it less labourous to chain many exception, and make it more explicit also.
|
|
|
Smart-ass quips aside, I think I agree with Brandon on this. Java doesn't need more exception cruft, it needs less. My guess is that Sun wanted Java to have mass appeal, which means it needed to try to be idiot-proof (at least a lot more so than C and C++). Hence, checked exceptions, with the thinking that API developers could force proper exception handling down the collective throats of the masses. Naturally, when it comes to defeating foolproof schemes, the fools reveal their genius -- and the world is filled with empty catch clauses.
When I was first exposed to checked exceptions -- "in the small," of course -- they seemed pretty darned nifty. The compiler would tell me exactly which exceptions I should be expecting from a particular call and I would dutifully handle them -- and, I even didn't have to do the footwork of looking them up in the documentation! However, after the novelty wears of (which is pretty quickly), it just amounts to a lot of extra busywork and distraction. I think it has a great anal-retentive appeal, but I question whether it makes for better code. In fact, it may lead to a false sense of "completeness" since it is entirely possible the API writer may have decided to throw RuntimeException s exclusively.
What I think would be ideal is all unchecked exceptions, as in C#, combined with a smart IDE that will do auto-completion of catch clauses, filling in all possible exceptions that could occur in the preceding try block (yes, it would need to look into calls of calls of calls and so on). This might not be feasible in a dynamic language like Python, but should be in Java or C#.
|
|
|
The line of Heilsberg's argumentation seems to be the following: "many programmers misuse checked exceptions, so let's remove the language support for them altogether". What kind of logic is this? This simply means acknowleding the sorts of described "worst practices" and letting them spread further.
Complaining about method signatures becoming fatter because of too many checked exceptions? Think about why you are passing all those weird exception types on to your clients! Making your clients deal with exceptions that stem from implementation details of your module is a design error.
|
|
|
Whether checked exceptions are useful or not depends on a more fundamental question: Useful for what?
If one's goal is to make a programming language popular for programmers (so that programmers start using it, and start to like it), then I agree that things should be kept simple - just don't cause many troubles for the programmer, just keep things simple and smooth. Then checked exceptions are more trouble than they're worth.
On the other hand, if one's goal is to build systems that can survive all kinds disasters and exceptional conditions without a blink, then checked exceptions are very useful, indeed. They're useful, because they give you a programmatic interface to failure. And that interface makes it possibly easier to recover from those failures.
Traditionally, the latter category of software contained things like operating systems, file systems, real-time systems, etc. For instance, it's hardly acceptable to just pop up a dialog box, "Sorry, disk sector XXX failed. Please reboot." Rather, you just want to hide that error from the user, and route around it. Indeed, disk controllers just handle such things themselves, mapping around bad disk sectors, without even letting an OS know about the problem. Traditionally, end-user applications are much less industrious: You can always pop up that message dialog and push exception handling up the chain to the user.
But the standards are changing, and end-user software will have to become as reliable and OSs, file systems, disks, or embedded systems. For instance, today it's still OK to pop up a dialog to the effect: "Can't save file. File system full." That may no longer be acceptable in a few years: People don't care about file systems, they just want the system to save their darn file (even if it means that the system now has to borrow another machine's file system perhaps).
As an increasing number of people use software, users' tolerance is going to diminish: They will demand software that can fix its own problems. In turn, they will demand that programmers actually consider the exceptional conditions AND write the software so it recovers from those exceptions.
This is already happening in the industry I am in. Displaying an error message "Cannot connect to database" might be acceptable in an environment that caters to programmers or system administrators. But displaying that message to end-users who don't know what a database is, or why you would have to "connect" to one, is going to be a serious problem. Such users lose patience very quickly, and dial the tech support line with great indignation: "Your system doesn't work. I'm losing money, I can't conduct business. Come out here right away, and fix this." They can't and won't follow instructions - they will want you to go out to their location and fix their problems, even if it means only a database reboot.
From a business point of view, that's going to be an extraordinarily unprofitable venture. So, instead, you probably want the program to declare that connection exception, and then think of all the ways your program can reconnect to that database, and create an exception handler that's activated by that connection exception. Or, if all fails, ship a log file back to your support office even BEFORE the customer notices the error (or at least before they call).
I like to declare only those exceptions where there is a reasonable chance of automated recovery. But once I declare those exceptions, that says, in essence, that the corresponding recovery routines are now a system requirement - the system doesn't meet its requirements without them.
I do agree that declaring exceptions from which automated recovery is hardly possible, is an undue burden, and such a practice completely obliterates the value of checked exceptions: It simply invites programmers to leave empty catch clauses in their code.
In summary, as the standard for software quality rises, checked exceptions will continue to play an important role in making software more robust and reliable. They can be an inmune system to software. And an abused immune system is just as bad as an organism without one altogether.
|
|
|
> I like to declare only those exceptions where there is a > reasonable chance of automated recovery. But once I > declare those exceptions, that says, in essence, that the > corresponding recovery routines are now a system > requirement - the system doesn't meet its requirements > without them.
Well said. The java language gives us a useful tool to describe error-handling requirements as part an interface. Like most tools, just because it's there doesn't mean you should use it every time. Deciding what to declare as checked exceptions is an important part of desigining an API.
The language feature should not be tossed out just because API designers are misusing it. As someone said earlier, if your interface includes all kinds of checked exceptions then it probably exposes too much of the internal implementation. And *that* is what leads to the versioning and scalability problems raised by Hejlsberg.
Some people have said that checked exceptions are an attempt to make java idiot-proof, a sort of dumbing-down approach to protect programmers from themselves. To me, that focuses on the wrong side of the equation. The goal of checked exceptions is not to make *using* an interface easier, it is to enable programmers to *design* more expressive interfaces.
|
|
|
> Some people have said that checked exceptions are an > attempt to make java idiot-proof, a sort of dumbing-down > approach to protect programmers from themselves. To me, > that focuses on the wrong side of the equation. The goal > of checked exceptions is not to make *using* an interface > easier, it is to enable programmers to *design* more > expressive interfaces.
The whole purpose of designing anything is so that it can be used, isn't it? What's the good of a complicated and hard-to-use API that affords the designers all the expressiveness they could possibly imagine? Programmers usually over-design things in the wrong places anyway, so why give them more ammunition?
More importantly, it doesn't necessarily follow that a more heavily embellished interface is a more expressive interface. I think Anders was saying that he saw no clear imperative for the type of implementation of checked exceptions seen in Java. That's not saying they are a bad thing, just that he didn't see them as a valuable enough feature to warrant inclusion in C#.
It is kind of interesting in this light that C# is much more restrictive with inheritance, requiring lots of pre-planning in the use of virtual, then subsequently, in derived classes new or override. It seems like this treatment is philosophically at odds with the checked exceptions treatment. Hopefully Bill and Bruce asked Anders about this apparent dichotomy in one of the later segments of the interview...
|
|
|
> The whole purpose of designing anything is so that it can > be used, isn't it? What's the good of a complicated and > hard-to-use API that affords the designers all the > expressiveness they could possibly imagine?
I think you're right -- a complicated interface is not necessarily easy to use. To argue checked exceptions are there to 'make things easy for programmers' just isn't true, because they don't really make things easy.
What the do allow for is an interface that conveys more information to the caller. As with any kind of information, sometimes more is useful and sometimes more is just distracting. And like everything else in software, it's up to the developer to walk that line.
On the other hand I don't think it follows that a less complex API is necessarily easy to use. public Object doSomething(Object[] theObjects) is certainly not complicated. But I suspect it's very difficult to use in any meaningful way.
Anyway, I seem to have gotten away from my original point. I agree that checked exceptions might not be worth propagating to new languages, or that a better mechanism to accomplish the same thing might be designed. I just haven't yet heard a convincing argument.
If we're worried that a method throws too many checked exceptions and therefore puts too much burden on the caller, than the method is poorly designed -- and it has nothing whatsoever to do with Java as a language. You could make the exact argument Anders is making about method parameters, or return codes, or anything else. 'This method I have to call takes 15 parameters, and with every release they add a new one that breaks all my code.' One would point out that the method probably shouldn't take 15 parameters. One would not say "a language should not allow you to specify more than one parameter to a method."
It's also worth mentioning that lots of languages are very powerful and useful precisely because they have different goals than Java with respect to precise interface design, maintainable and self-documenting code, etc. If I'm writing perl, I don't want to deal with checked exceptions. This isn't because they are useless, it's because I'm seeking a different end result. But for a language that is oriented towards interface design, allowing interfaces to specify out-of-band information in the form of exceptions is very useful.
|
|
|
... Anders Hejlsberg point of view shows once again that microsoft just wants to make junior programmer being able to write bad code!
I wonder if he read the chapter about how to use the java exceptions handling mechanisms in Joshua Bloch's Effective Java Programming Language Guide.
|
|
|
I have the suspicion that that Anders is talking mostly about exception handling in simple user interfaces where a global approach might not be so bad as long as an underlying C++ library is handling the real problems in the program. If you see C# as a replacement to visual basic I can understand this mindset. However as was mentioned earlier, when you think about using Java or C# for more system level problems, I think the context afforded by handling a checked exception is crucial.
I also think that the exponential growth of declared exceptions is really a bogus argument. I think people felt this way back in Java1.0 days, when design of an exception hierarchy might not have been considered an integral part of program logic. But now I think people quite commonly make use of polymorphic exceptions and exception chaining. In fact I am quite puzzled that none of the interview nor the followup discussion discussed either of these two features of java exception handling. If an underlying library throws too many exceptions, chain them in a cleaner excpetion that your code knows about. If you throw too many low level exceptions that dont share common ancestry in your own code, thats your fault.
|
|
|
>"Adding a new exception to a throws clause in a new version breaks client code. "
Adding a new exception to a throws clause doe NOT break client code(does not break binary compatibility).Its embarrassing that the interviewers/tech editors did not catch this.
>"You don't want a program where in 100 different places you handle exceptions and pop up error dialogs. " ..."The exception handling should be centralized,"
Refactorings/Design patterns can be used centralize exception handling.See: <a href="http://www.refactoring.com/catalog/extractMethod.html">Extract Method</a> or even use <a href="http://www.javaworld.com/javaworld/jw-04-2003/jw-0425-designpattern s.html">Simple Singleton</a> if you must.
>"...people do ridiculous things. For example, they decorate every method with, "throws Exception... [OR]...You end up having to declare 40 exceptions that you might throw. " Forty exceptions in a throws clause? This interview is sounds more like a troll.
"I can't tell you how many times I've seen thisthey say, try, da da, catch curly curly."
I have seen empty catch blocks in poorly written overly verbose intro to Java books. Where have you seen such awful code?
Checked exceptions are one of the reasons why Java is better than C# - <a href="http://www.freeroller.net/page/ceperez/20030129?catname=101%20List">Re ason #64</a> Would rather rely on documentation than have the compiler let you know of a possible exception?
"For example, lots of newbie's coming in from the C world complain about exceptions and the fact that they have to put exception handling all over the place - they want to just write their code. But that's stupid: most C code never checks return codes and so it tends to be very fragile. If you want to build something really robust, you need to pay attention to things that can go wrong and most folks don't in the C world because it's just to damn hard. One of the design principles behind Java is that I don't care much about how long it takes to slap together something that kinda works. The real measure is how long it takes to write something solid. Lots have studies have been done on developer productivity, and Java beats C and C++ by a factor of 2...." - <a href="http://weblogs.java.net/jag/">James Gosling</a>
I'm a strong believer that if you don't have anything right to say, or anything that moves the art forward, then you'd better just be completely silent
Here Here!!
|
|
|
> One of the design principles behind Java is that I don't > care much about how long it takes to slap together > something that kinda works. The real measure is how long > it takes to write something solid. ." > - <a href="http://weblogs.java.net/jag/">James > Gosling</a>
I was not aware of this blog by Gosling, but it really speaks to the heart of the issue. I found this out when I moved from doing primarly consulting to building and shipping an end-user product. When you have a product to support, all the short-cuts, all the sloppiness, all the unwritten tests, etc., that you thought were OK in that late-night hour's moment of exhaustion - will come back to haunt you with a vengeance. It will take MUCH longer, and cost many times more, to have to come back and fix problems with a product that's in the customer's hands. In fact, that just might mean the difference whether a small firm can "swim across" (to paraphrase Andy Grove), or whether it will run out of steam mid-lake.
Gosling's comment is what I think we should use as a benchmark when judging a programming language: How long it takes to create something robust with a given language. Since most programming languages are Turing-complete, you can pretty much use any language to write any program you wish. I used to think that I was productive with a language when I could quickly put something together that just sort of worked. But there is a difference between "sort of works" and "bet-my-money-on-it" works. Now I care much more about the latter than the former.
|
|
|
> Its embarrassing that the interviewers/tech editors did not catch this.
Probably wasn't in the throws clause.
|
|
|
> Gosling's comment is what I think we should use as a > benchmark when judging a programming language: How long it > takes to create something robust with a given language. > Since most programming languages are Turing-complete, you > can pretty much use any language to write any program you > wish. I used to think that I was productive with a > language when I could quickly put something together that > just sort of worked. But there is a difference between > "sort of works" and "bet-my-money-on-it" works. Now I care > much more about the latter than the former.
But what evidence is there that Java programs are, on the average, more robust than programs in every other language, because of checked exceptions? It may be that this is a bit like the static vs. dynamic type language divide -- some people feel more comfortable with static types and think that such code must be more robust. I think you only get robust programs from hard work and extraordinary care, regardless of language features.
|
|