June 25, 2017 Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
I am disappointed that D doesn't have checked exceptions. C++ and C# also don't have checked exceptions. Java has checked exceptions. Having programmed extensively in all these languages I can say with confidence that checked exceptions is the most important thing missing in C++ and C#. Around the time C# was released there was a lot of debate around the topic of checked vs unchecked exceptions and this created an impression that Java's use of checked exceptions was "controversial". In fact it is a feature every modern language should have. Exceptions that can be thrown by a method should be part of the contract of that method. Changing the list of the list of exceptions that can be thrown by a method is just like changing the parameters of the method. This is something that should cause calling code to fail to compile. This will allow you to inspect the calling code and decode how to deal with the new exception. When the exception that can be thrown by a method is not known you don't know what exceptions to catch and what exceptions to pass through to callers. You can't rely on documentation because the documentation is not verified be the compiler and so is not reliable. All you can do is to run the program a few times, see what exceptions you get and handle them. Even if you are able to determine the full list of exceptions using this strategy, a future revision of the called method can throw a new exception and cause your program to crash. As a result, in large C# code bases it is common to see catching the base Exception class because this is the only way to prevent a crash. This causes exceptions to be "swallowed" because higher level code that is actually prepared to handle certain exceptions never get the exception. With Java this problem doesn't exist. When you call a function you know exactly what exceptions can be thrown and you can catch (or pass through) exactly those exceptions. There is no need to catch the base exception class. When you have multiple implementations of an interface (such as database connectivity layer) and each of those implementations can throw a completely disjoint set of exceptions there is no way to write polymorphic code that can recover from errors. Anders Hejlsberg, designer of C# doesn't think people care to catch specific exceptions, which is why C# doesn't have checked exceptions. (See http://www.artima.com/intv/handcuffs.html ) "They're not going to handle any of these exceptions. There's a bottom level exception handler around their message loop. That handler is just going to bring up a dialog that says what went wrong and continue." Also: "The exception handling should be centralized.." In other words, he thinks all people want to do with exceptions is catch the base Exception class and display a message. I have a lot of respect for Anders Hejlsberg (my first programming language was Turbo Pascal) but he is completely wrong on this topic. Regarding the versioning issue discussed by Anders Hejlsberg, my response is that throwing a new exception is like changing the signature of a function. You want callers to be alerted that they need to update their code! This means the calling code should fail to compile until it is updated. |
June 25, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to mckoder | http://forum.dlang.org/post/ullvxbfqeuztwecxcygb@forum.dlang.org On Sunday, 25 June 2017 at 17:38:14 UTC, mckoder wrote: > I am disappointed that D doesn't have checked exceptions. > > C++ and C# also don't have checked exceptions. Java has checked exceptions. Having programmed extensively in all these languages I can say with confidence that checked exceptions is the most important thing missing in C++ and C#. Around the time C# was released there was a lot of debate around the topic of checked vs unchecked exceptions and this created an impression that Java's use of checked exceptions was "controversial". In fact it is a feature every modern language should have. > > Exceptions that can be thrown by a method should be part of the contract of that method. Changing the list of the list of exceptions that can be thrown by a method is just like changing the parameters of the method. This is something that should cause calling code to fail to compile. This will allow you to inspect the calling code and decode how to deal with the new exception. > > When the exception that can be thrown by a method is not known you don't know what exceptions to catch and what exceptions to pass through to callers. You can't rely on documentation because the documentation is not verified be the compiler and so is not reliable. All you can do is to run the program a few times, see what exceptions you get and handle them. Even if you are able to determine the full list of exceptions using this strategy, a future revision of the called method can throw a new exception and cause your program to crash. As a result, in large C# code bases it is common to see catching the base Exception class because this is the only way to prevent a crash. This causes exceptions to be "swallowed" because higher level code that is actually prepared to handle certain exceptions never get the exception. > > With Java this problem doesn't exist. When you call a function you know exactly what exceptions can be thrown and you can catch (or pass through) exactly those exceptions. There is no need to catch the base exception class. > > When you have multiple implementations of an interface (such as database connectivity layer) and each of those implementations can throw a completely disjoint set of exceptions there is no way to write polymorphic code that can recover from errors. > > Anders Hejlsberg, designer of C# doesn't think people care to catch specific exceptions, which is why C# doesn't have checked exceptions. (See http://www.artima.com/intv/handcuffs.html ) "They're not going to handle any of these exceptions. There's a bottom level exception handler around their message loop. That handler is just going to bring up a dialog that says what went wrong and continue." Also: "The exception handling should be centralized.." In other words, he thinks all people want to do with exceptions is catch the base Exception class and display a message. I have a lot of respect for Anders Hejlsberg (my first programming language was Turbo Pascal) but he is completely wrong on this topic. > > Regarding the versioning issue discussed by Anders Hejlsberg, my response is that throwing a new exception is like changing the signature of a function. You want callers to be alerted that they need to update their code! This means the calling code should fail to compile until it is updated. |
June 25, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to mckoder | On Sunday, 25 June 2017 at 17:38:14 UTC, mckoder wrote: > Exceptions that can be thrown by a method should be part of the contract of that method. Changing the list of the list of exceptions that can be thrown by a method is just like changing the parameters of the method. This is something that should cause calling code to fail to compile. This will allow you to inspect the calling code and decode how to deal with the new exception. I think the reason it didn't work out for C++ to specify the exceptions is that you need good IDE support for it and C++ was to a large extent a language where people wrote code in basic editors. > message. I have a lot of respect for Anders Hejlsberg (my first programming language was Turbo Pascal) but he is completely wrong on this topic. Turbo Pascal as a language was so-so, but the IDE was very productive. |
June 26, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Eugene Wissner | On Sunday, 25 June 2017 at 18:00:46 UTC, Eugene Wissner wrote:
> http://forum.dlang.org/post/ullvxbfqeuztwecxcygb@forum.dlang.org
>
As suggested by the post in above link I searched for "Walter checked exceptions". I found a few posts where Walter points to an article written by Bruce Eckel. Though the link to Eckel's article no longer works, I am familiar with Eckel's arguments, and I even exchanged emails with Eckel back in 2003 on this topic. Suffice to say I believe Eckel is wrong on this topic.
Bruce Eckel argues that checked exceptions forces you to write bad code (catch Exception base class) and Walter mentions this. Eckel is wrong. The opposite is true. Lack of checked exceptions forces you to catch Exception base class in order to prevent crashes. If you know what exceptions are possible then you can catch just those exceptions. If you don't know then you have to rely on testing to find out what exceptions are possible but you will never get an exhaustive list through testing since tests can never be 100% comprehensive for non-trivial programs. So you end up catching Exception base class, thus swallowing exceptions. I have seen lots of large code bases in C# and Java written by very good developers, and I can tell you that experience has proven that "catch (Exception)" is common in C# code bases and rarely seen in Java code bases.
|
June 26, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to mckoder | On 6/25/17 1:38 PM, mckoder wrote:
> I am disappointed that D doesn't have checked exceptions.
>
> C++ and C# also don't have checked exceptions. Java has checked exceptions. Having programmed extensively in all these languages I can say with confidence that checked exceptions is the most important thing missing in C++ and C#. Around the time C# was released there was a lot of debate around the topic of checked vs unchecked exceptions and this created an impression that Java's use of checked exceptions was "controversial". In fact it is a feature every modern language should have.
No, checked exceptions leads to this (maybe not for you, but for 90% of developers out there):
void foo()
{
functionWithException();
}
compiler: foo throws, and you need to handle or declare the exceptions it throws
void foo()
{
try {
functionWithException();
} catch(Exception e) {} // shut up compiler
}
So it ends up defeating the purpose. The exception is not properly handled, either inside or outside the function.
You can get into a long discussion if you want, I'm not going there. Bottom line: D *will not* have checked exceptions, Walter has said so many times. If that's a deal killer, you should probably stick with Java. Sorry, don't want to waste your time.
-Steve
|
June 26, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Monday, 26 June 2017 at 15:15:54 UTC, Steven Schveighoffer wrote:
>
> No, checked exceptions leads to this (maybe not for you, but for 90% of developers out there):
>
> void foo()
> {
> functionWithException();
> }
>
> compiler: foo throws, and you need to handle or declare the exceptions it throws
>
> void foo()
> {
> try {
> functionWithException();
> } catch(Exception e) {} // shut up compiler
> }
>
Why wouldn't you instead write:
void foo() throws Exception // shut up compiler
{
functionWithException();
}
That's easier, and no worse than C# even though you have defeated checked exceptions.
Here's the point: with checked exceptions good programmers can write good code. Without checked exceptions even good programmers are forced to write bad code. It is impossible to prevent bad programmers from writing bad code, so that should not even be a goal, but enabling good programmers to write good code should be a goal.
|
June 26, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Monday, 26 June 2017 at 15:15:54 UTC, Steven Schveighoffer wrote:
> void foo()
> {
> try {
> functionWithException();
> } catch(Exception e) {} // shut up compiler
> }
>
> So it ends up defeating the purpose. The exception is not properly handled, either inside or outside the function.
That's a poor argument, this will be caught in code reviews. You might as well use the same argument against returning error codes or optionals. Yes, it is possible to ignore them… and?
|
June 26, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Monday, 26 June 2017 at 15:15:54 UTC, Steven Schveighoffer wrote:
>
> No, checked exceptions leads to this (maybe not for you, but for 90% of developers out there):
>
> void foo()
> {
> functionWithException();
> }
>
> compiler: foo throws, and you need to handle or declare the exceptions it throws
>
> void foo()
> {
> try {
> functionWithException();
> } catch(Exception e) {} // shut up compiler
> }
>
Just curious: how are checked exceptions different from setting nothrow as the default? Like you would have to write:
void foo() @maythrow
{
functionWithException();
}
So for instance, you could still use your "//shut up compiler" code with the nothrow default.
|
June 26, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to jmh530 | On Monday, 26 June 2017 at 16:35:51 UTC, jmh530 wrote:
> Just curious: how are checked exceptions different from setting nothrow as the default? Like you would have to write:
>
> void foo() @maythrow
> {
> functionWithException();
> }
>
> So for instance, you could still use your "//shut up compiler" code with the nothrow default.
Checked exceptions allow a lot more precision about what types of exceptions a function can throw.
|
June 26, 2017 Re: Checked vs unchecked exceptions | ||||
---|---|---|---|---|
| ||||
Posted in reply to jmh530 | On Monday, 26 June 2017 at 16:35:51 UTC, jmh530 wrote:
> Just curious: how are checked exceptions different from setting nothrow as the default? Like you would have to write:
>
> void foo() @maythrow
> {
> functionWithException();
> }
>
> So for instance, you could still use your "//shut up compiler" code with the nothrow default.
I think the basic argument was that if throws-anything is the default expectation then people won't feel like silencing thrown exceptions, but allow them to propagate freely. Of course, if the function is marked nothrow then they still will have to silence any exceptions before returning, so same issue.
But, I am pretty convinced that this has more to do with tooling than usability. If the tooling is not created with evolving exceptions spec in mind then it becomes tedious to update the throw specification for functions higher up in the call-chain.
Another issue is that a function that takes a lambda/function as parameters will have to cover any exception that the parameter lambdas/function can throw as well. Which actually might be a good thing, as it forces you to think more clearly about where exceptions originate from: what-if-the-foreign-lambda throws an exception?
But it does have implications for how you work.
|
Copyright © 1999-2021 by the D Language Foundation