www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Checked vs unchecked exceptions

reply mckoder <mckoder yahoo.com> writes:
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.
Jun 25
next sibling parent reply Eugene Wissner <belka caraus.de> writes:
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.
Jun 25
parent mckoder <mckoder yahoo.com> writes:
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.
Jun 26
prev sibling next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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.
Jun 25
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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
Jun 26
next sibling parent reply mckoder <mckoder yahoo.com> writes:
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.
Jun 26
next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Monday, 26 June 2017 at 15:40:19 UTC, mckoder wrote:
 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.
Because that's even worse. The `try catch` as least takes responsibility for handling the error - even if it is just intentionally ignoring it. Your version delegates the responsibility up the call chain (which is in and of itself fine), but it - as you even stated - prevents the caller from using checked exceptions, i.e. you're cheating the system.
 Here's the point: with checked exceptions good programmers can 
 write good code.
With checked exceptions any programmer is forced to a) annotate every single function with the complete aggregate of the exceptions that may be thrown by itself or the functions it calls b) violate checked exceptions and limit its callers by marking itself as throwing a parent exception Or, more succinct: You must either manually write things down the compiler could find out in a fraction of the time via static analysis, or cheat the system; both cases are bad code.
Jun 26
next sibling parent reply jag <jagseattle hotmail.com> writes:
On Monday, 26 June 2017 at 17:43:08 UTC, Moritz Maxeiner wrote:
 Here's the point: with checked exceptions good programmers can 
 write good code.
With checked exceptions any programmer is forced to a) annotate every single function with the complete aggregate of the exceptions that may be thrown by itself or the functions it calls b) violate checked exceptions and limit its callers by marking itself as throwing a parent exception
Here's an example C# pseudocode to illustrate the problem: Programmer A writes this C# code: class A { public static void startFoo() { if (/* Foo is not installed */) throw new FooNotInstalled(); // ... } } Programmer B calls the above code like this: class B { try { A.startFoo(); } catch (FooNotInstalled) { // Tell user to purchase Foo } } Later programmer A updates his code because there are newer versions of Foo and he needs the newest version: class A { public static void startFoo() { if (/* Foo is not installed */) throw new FooNotInstalled(); if (/* Foo version is too old */) throw new FooVersionTooOld(); // ... } } Now the code written by Programmer B crashes even though it compiles file. That's bad. Had this been Java, programmer would be would be alerted to the fact that he needs to decide what do do if the version of Foo is too old. This is good. So listing exceptions that can be thrown is a good thing because it helps you write more reliable code. If you are lazy you can always defeat the system by declaring your method as throwing a parent (or the root) exception class, in which case it is no worse than C#.
Jun 26
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Monday, 26 June 2017 at 18:31:50 UTC, jag wrote:
 On Monday, 26 June 2017 at 17:43:08 UTC, Moritz Maxeiner wrote:
 Here's the point: with checked exceptions good programmers 
 can write good code.
With checked exceptions any programmer is forced to a) annotate every single function with the complete aggregate of the exceptions that may be thrown by itself or the functions it calls b) violate checked exceptions and limit its callers by marking itself as throwing a parent exception
Here's an example C# pseudocode to illustrate the problem:
No need, you can assume the people discussing this are familiar with the issue, it's not new.
 class A {
    public static void startFoo() {
       if (/* Foo is not installed */)
           throw new FooNotInstalled();
       // ...
    }
 }

 Programmer B calls the above code like this:

 class B {
    try {
       A.startFoo();
    }
    catch (FooNotInstalled) {
       // Tell user to purchase Foo
    }
 }

 Later programmer A updates his code because there are newer 
 versions of Foo and he needs the newest version:

 class A {
    public static void startFoo() {
       if (/* Foo is not installed */)
           throw new FooNotInstalled();
       if (/* Foo version is too old */)
           throw new FooVersionTooOld();
       // ...
    }
 }

 Now the code written by Programmer B crashes even though it 
 compiles file. That's bad.

 Had this been Java, programmer would be would be alerted to the 
 fact that he needs to decide what do do if the version of Foo 
 is too old. This is good.
And the good *way* to achieve this result would be the following: - When visiting `startFoo`, the compiler automatically aggregates all different exceptions it may throw and stores the resulting set - If `startFoo` is going to be part of a (binary) library and its symbol is exported, also export its exception set - Improve the compiler's nothrow analysis such that if startFoo is called in scope S, but all of the exceptions in its exception set are caught (i.e. can't break out of scope S), it is treated as nothrow in S. - Enclose the call to `startFoo` in B in a nothrow scope.
 So listing exceptions that can be thrown is a good thing 
 because it helps you write more reliable code.
It is a bad thing because you force a human to do a machine's job.
Jun 26
next sibling parent reply crimaniak <crimaniak gmail.com> writes:
On Monday, 26 June 2017 at 19:31:53 UTC, Moritz Maxeiner wrote:
 And the good *way* to achieve this result would be the 
 following:
 - When visiting `startFoo`, the compiler automatically 
 aggregates all different exceptions it may throw and stores the 
 resulting set
 - If `startFoo` is going to be part of a (binary) library and 
 its symbol is exported, also export its exception set
 - Improve the compiler's nothrow analysis such that if startFoo 
 is called in scope S, but all of the exceptions in its 
 exception set are caught (i.e. can't break out of scope S), it 
 is treated as nothrow in S.
 - Enclose the call to `startFoo` in B in a nothrow scope.
After preparing my message I read tail of the thread and see your vision very close to mine. đź‘Ť
 So listing exceptions that can be thrown is a good thing 
 because it helps you write more reliable code.
It is a bad thing because you force a human to do a machine's job.
This is a bad necessity, but a necessary opportunity. Sometimes you need to be sure that the compiler's vision matches yours.
Jun 26
parent Moritz Maxeiner <moritz ucworks.org> writes:
On Tuesday, 27 June 2017 at 00:29:53 UTC, crimaniak wrote:
 On Monday, 26 June 2017 at 19:31:53 UTC, Moritz Maxeiner wrote:
 And the good *way* to achieve this result would be the 
 following:
 - When visiting `startFoo`, the compiler automatically 
 aggregates all different exceptions it may throw and stores 
 the resulting set
 - If `startFoo` is going to be part of a (binary) library and 
 its symbol is exported, also export its exception set
 - Improve the compiler's nothrow analysis such that if 
 startFoo is called in scope S, but all of the exceptions in 
 its exception set are caught (i.e. can't break out of scope 
 S), it is treated as nothrow in S.
 - Enclose the call to `startFoo` in B in a nothrow scope.
After preparing my message I read tail of the thread and see your vision very close to mine. đź‘Ť
I wouldn't call it a vision, since I personally don't need it. It's just that I consider checked exceptions as an excuse for not improving a deficient compiler.
 So listing exceptions that can be thrown is a good thing 
 because it helps you write more reliable code.
It is a bad thing because you force a human to do a machine's job.
This is a bad necessity, but a necessary opportunity. Sometimes you need to be sure that the compiler's vision matches yours.
I would need a concrete example that's not either solved by the steps mentioned in my post, or using compile time introspection on a function's exception set (as shown in John's idiomatic proposal [1]). [1] http://forum.dlang.org/post/lxejskhonjtiifvvgwnd forum.dlang.org
Jun 26
prev sibling parent reply Biotronic <simen.kjaras gmail.com> writes:
On Monday, 26 June 2017 at 19:31:53 UTC, Moritz Maxeiner wrote:
 the good *way* to achieve this result would be the following:
 - When visiting `startFoo`, the compiler automatically 
 aggregates all different exceptions it may throw and stores the 
 resulting set
 - If `startFoo` is going to be part of a (binary) library and 
 its symbol is exported, also export its exception set
 - Improve the compiler's nothrow analysis such that if startFoo 
 is called in scope S, but all of the exceptions in its 
 exception set are caught (i.e. can't break out of scope S), it 
 is treated as nothrow in S.
 - Enclose the call to `startFoo` in B in a nothrow scope.
So I have this .dll. How do I specify which exceptions it throws? -- Biotronic
Jun 27
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 28 June 2017 at 06:31:40 UTC, Biotronic wrote:
 On Monday, 26 June 2017 at 19:31:53 UTC, Moritz Maxeiner wrote:
 the good *way* to achieve this result would be the following:
 - When visiting `startFoo`, the compiler automatically 
 aggregates all different exceptions it may throw and stores 
 the resulting set
 - If `startFoo` is going to be part of a (binary) library and 
 its symbol is exported, also export its exception set
 - Improve the compiler's nothrow analysis such that if 
 startFoo is called in scope S, but all of the exceptions in 
 its exception set are caught (i.e. can't break out of scope 
 S), it is treated as nothrow in S.
 - Enclose the call to `startFoo` in B in a nothrow scope.
So I have this .dll. How do I specify which exceptions it throws?
- Static/Dynamic linking: As said in bullet point two above, the exception set would have to be exported (more precise: in such a way that it can be loaded again at compile time); there are several ways to go about that: Add the exception set (e.g. via attributes) to the function declarations in a .di file (which could indeed look like checked exception, except that it's auto generated), use a separate (binary) file with mangled functions names to exception set mapping, etc. - Dynamic loading: Won't work One could also make an exception for bodyless functions and allow specification of the exception set *only* there, e.g. --- // Allow "checked exceptions" for stubs only void foo() throws AException throws BException; ---
Jun 28
next sibling parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Wed, 28 Jun 2017 11:04:58 +0000
schrieb Moritz Maxeiner <moritz ucworks.org>:

 One could also make an exception for bodyless functions and allow 
 specification of the exception set *only* there, e.g.
 
 ---
 // Allow "checked exceptions" for stubs only
 void foo() throws AException throws BException;
 ---
Ah, come one, now that you see the need you can also embrace it as an option for people who want to add some documentation. void foo() throws AException // When the ant's life ends prematurely throws BException; // When the ant gets distracted by a lady bug -- Marco
Jul 05
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 5 July 2017 at 20:35:14 UTC, Marco Leise wrote:
 Am Wed, 28 Jun 2017 11:04:58 +0000
 schrieb Moritz Maxeiner <moritz ucworks.org>:

 One could also make an exception for bodyless functions and 
 allow specification of the exception set *only* there, e.g.
 
 ---
 // Allow "checked exceptions" for stubs only
 void foo() throws AException throws BException;
 ---
Ah, come one, now that you see the need you can also embrace it as an option for people who want to add some documentation.
After some more consideration I would be amenable to automatic inference of the exception set as the default with *optional* manual specification, i.e. --- void foo() throws AException throws BException { ... } vod bar() { foo(); } --- works and bar's exception is inferred by the compiler to contain AException and BException. But to be clear (and the title and description of any DIP addressing this should reflect this): These are not checked exceptions, because checked exceptions would require bar to declare its exception set manually.
Jul 05
next sibling parent crimaniak <crimaniak gmail.com> writes:
On Thursday, 6 July 2017 at 01:31:44 UTC, Moritz Maxeiner wrote:
 ---
 void foo() throws AException throws BException { ... }
 vod bar() { foo(); }
 ---

 works and bar's exception is inferred by the compiler to 
 contain AException and BException.

 But to be clear (and the title and description of any DIP 
 addressing this should reflect this):
 These are not checked exceptions, because checked exceptions 
 would require bar to declare its exception set manually.
Not checked in sense of Java, but Java has no deductible exceptions. If compiler will fail on `void bar() { foo(); } throws AException;` we still can call it 'checked', I think. In sense of D.
Jul 06
prev sibling parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Thu, 06 Jul 2017 01:31:44 +0000
schrieb Moritz Maxeiner <moritz ucworks.org>:

 But to be clear (and the title and description of any DIP 
 addressing this should reflect this):
 These are not checked exceptions, because checked exceptions 
 would require bar to declare its exception set manually.
Yep, absolutely clear. Just like "auto a = 1" does not declare a variable as we all know declarations start with a type. Instead of defining checked exceptions how it bests fits all your posts in this thread, why not say "Checked exceptions as implemented in Java were a good idea, had they allowed the compiler to infer them where possible." Of course we can still call those inferred checked exceptions something else. ;o) -- Marco
Jul 06
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Thursday, 6 July 2017 at 11:01:26 UTC, Marco Leise wrote:
 Am Thu, 06 Jul 2017 01:31:44 +0000
 schrieb Moritz Maxeiner <moritz ucworks.org>:

 But to be clear (and the title and description of any DIP
 addressing this should reflect this):
 These are not checked exceptions, because checked exceptions
 would require bar to declare its exception set manually.
Yep, absolutely clear. Just like "auto a = 1" does not declare a variable as we all know declarations start with a type.
Red herring. Checked exceptions are well defined - not just implemented - (by Java as the de facto authority for the term) as requiring the declaration of all exceptions (that aren't inherited from some special class, in Java's case `RuntimeException`, in D's it would be `Error`) that may be thrown by a function as a result of its body being executed [1]. If even a single function is allowed to have its exception set defined by inference (instead of declaration), you are not implementing checked exceptions. --- void foo() throws AExp throws BExc { ... } void bar1() { foo(); } // Checked exceptions require this to result in a compilation error void bar2() throws AExc throws BExc { foo(); } // this must be used for checked exceptions ---
 Instead of defining checked exceptions how it bests fits all
 your posts in this thread, why not say "Checked exceptions as
 implemented in Java were a good idea, had they allowed the
 compiler to infer them where possible."
Invalid premise. The definition of checked exceptions is de facto fixed by Java [1], because it not only coined the term but remains the only major PL to use them.
 Of course we can still call those inferred checked exceptions
 something else. ;o)
The java compiler does infer an exception set for every function based on its body. This is then checked against the function's exception set as declared in its signature. "inferred checked exceptions" is thus an oxymoron, because it would mean checking the inferred exception set against itself. [1] https://docs.oracle.com/javase/7/docs/api/java/lang/Exception.html
Jul 06
parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Thu, 06 Jul 2017 13:16:23 +0000
schrieb Moritz Maxeiner <moritz ucworks.org>:

 On Thursday, 6 July 2017 at 11:01:26 UTC, Marco Leise wrote:
 Am Thu, 06 Jul 2017 01:31:44 +0000
 schrieb Moritz Maxeiner <moritz ucworks.org>:
 =20
 But to be clear (and the title and description of any DIP
 addressing this should reflect this):
 These are not checked exceptions, because checked exceptions
 would require bar to declare its exception set manually. =20
Yep, absolutely clear. Just like "auto a =3D 1" does not declare a variable as we all know declarations start with a type. =20
=20 Red herring. [=E2=80=A6] =20 --- void foo() throws AExp throws BExc { ... } void bar1() { foo(); } // Checked exceptions require this to=20 result in a compilation error void bar2() throws AExc throws BExc { foo(); } // this must be=20 used for checked exceptions ---
You are right, it was a red herring. The code example makes it very obvious that inference means letting the exceptions slip through unchecked and out of main() in the wildest case.
 Invalid premise. The definition of checked exceptions is de facto=20
 fixed by Java [1], because it not only coined the term but=20
 remains the only major PL to use them.
That's right, but still one can distill general ideas and leave implementations details aside. Pretty much like the Platonic Ideal. Then you look at what the complaints are with the current implementation and see if you can satisfy all sides. I don't know if this is any good beyond an example of a different implementation of checked exceptions, but here is one option against the "every function in the call chain accumulates more and more 'throws' declarations": /** * Throws: * ZeroException when i is 0 * NonZeroException when i is not 0 */ void foo(int i) { if (i =3D=3D 0) throw new ZeroException(); throw new NonZeroException(); } void bar(int i) check_exceptions { foo(i); // Error: The following exceptions are not handled: // ZeroException thrown from foo() when i is 0 // NonZeroException thrown from foo() when i is not 0 } I.e. everything stays the same until a programmer needs a verification of what (s)he should/could handle right away, what needs to be wrapped and what can be passed further up the call chain. That's close to impossible now in deeply nested code. Resource unavailability prone to race conditions can often be handled by asking the user to fix the issue and continue for example (including network, disk space, RAM, video encoding hardware slots, exclusive microphone use). In other cases an exception is only thrown when an incorrect argument is passed. Knowing (statically) that you pass only good values you can catch the exception and turn it into an assert instead of passing it up the call chain, potentially allowing the caller to be nothrow. --=20 Marco
Jul 07
parent Moritz Maxeiner <moritz ucworks.org> writes:
On Friday, 7 July 2017 at 18:48:31 UTC, Marco Leise wrote:
 Am Thu, 06 Jul 2017 13:16:23 +0000
 schrieb Moritz Maxeiner <moritz ucworks.org>:

 That's right, but still one can distill general ideas and leave 
 implementations details aside. Pretty much like the Platonic 
 Ideal. Then you look at what the complaints are with the 
 current implementation and see if you can satisfy all sides.
The requirement checked exceptions impose on all functions to declare their respective exception set is not an implementation detail, but part of the definition. With regards to the general ideas: That's what my writing about exception sets, tehir inference, and nothrow analysis were (supposed to be) about. Unless, however, whatever one eventually tries to get into the language (if that is even attempted) conforms to the preexisting definition of checked exceptions (which I'm as certain as I can reasonably be won't make it into D), calling it that will only serve to cloud the issue and alienate people. Personally, I would call a system with the exception set of a function being (optionally) declarable as "declarable exceptions", but that might still be too close to "checked exceptions" not to illicit screams of horror.
 [...]

 I.e. everything stays the same until a programmer needs a 
 verification of what (s)he should/could handle right away, what 
 needs to be wrapped and what can be passed further up the call 
 chain. That's close to impossible now in deeply nested code.
If one replaces the ` check_exceptions` with `nothrow` the above is essentially what one should get if one enhances the (static) nothrow analysis (as I mentioned in an earlier post) to essentially treat a scope implicitly as nothrow if the aggregated exception set (as determined by the compiler) is empty. Or more succinct: Infer `nothrow` for normal functions, as well.
 In other cases an exception is only thrown when an incorrect 
 argument is passed. Knowing (statically) that you pass only 
 good values you can catch the exception and turn it into an 
 assert instead of passing it up the call chain, potentially 
 allowing the caller to be nothrow.
There are two cases here: - the function takes untrusted user data: Violations are valid runtime behaviour -> Exceptions, error codes, split in validation and processing function pair with the second being executed only after the first has successfully validated the input data (and thus transformed it into trusted program data), etc. - the function takes trusted program data: Violations are bugs -> assert, Error, DbC, etc.
Jul 07
prev sibling parent Marco Leise <Marco.Leise gmx.de> writes:
In general, I'm of the opinion that DLL API should be fully
explicit. It's the only way you can reasonably provide
ABI stability. That means no templates, no attribute inference
and explicit exception lists if the compiler were to used them.

-- 
Marco
Jul 05
prev sibling parent reply =?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Moritz Maxeiner <moritz ucworks.org> wrote:
 [...]
 Or, more succinct: You must either manually write things down the 
 compiler could find out in a fraction of the time via static 
 analysis, or cheat the system; both cases are bad code.
It's not at all bad code to write things down that the compiler could infer, quite the opposite. Writing it down signals _intent_ and the compiler can check if the implementation is matching the specification which gives you additional security. Additionally it allowes the compiler to do the checks locally which is much easier. Function signatures are interfaces which should be self-contained IMO, i.e. it should not be necessary to examine the function body. That's what signatures are for. At very least for public interfaces. I honestly don't understand how people that care a great deal about expressive type systems can be so opposed to checked exceptions. After all they wouldn't use 'object' for everything either. Tobi
Jun 26
next sibling parent reply mckoder <mckoder yahoo.com> writes:
On Tuesday, 27 June 2017 at 06:10:52 UTC, Tobias MĂĽller wrote:
 It's not at all bad code to write things down that the compiler 
 could infer, quite the opposite.
 Writing it down signals _intent_ and the compiler can check if 
 the implementation is matching the specification which gives 
 you additional security.
 Additionally it allowes the compiler to do the checks locally 
 which is much easier.

 Function signatures are interfaces which should be 
 self-contained IMO, i.e. it should not be necessary to examine 
 the function body. That's what signatures are for. At very 
 least for public interfaces.

 I honestly don't understand how people that care a great deal 
 about expressive type systems can be so opposed to checked 
 exceptions. After all they wouldn't use 'object' for everything 
 either.
Bravo! These are very important points! FWIW, Bruce Eckel who is so often quoted by people who are opposed to checked exceptions comes from a dynamic language background. Here's a relevant quote by Bruce Eckel: "I think that the belief that everything needs strong static (compile-time) checking is an illusion; it seems like it will buy you more than it actually does. But this is a hard thing to see if you are coming from a statically-typed language." Source: https://groups.google.com/forum/#!original/comp.lang.java.advocacy/r8VPk4deYDI/qqhL8g1uvf8J If you like dynamic languages such as Python you probably agree with Bruce Eckel. If you are a fan of static checking then I don't see how you can like that.
Jun 27
parent reply Sebastien Alaiwan <ace17 free.fr> writes:
On Tuesday, 27 June 2017 at 10:18:04 UTC, mckoder wrote:
 "I think that the belief that everything needs strong static 
 (compile-time) checking is an illusion; it seems like it will 
 buy you more than it actually does. But this is a hard thing to 
 see if you are coming from a statically-typed language."

 Source: 
 https://groups.google.com/forum/#!original/comp.lang.java.advocacy/r8VPk4deYDI/qqhL8g1uvf8J

 If you like dynamic languages such as Python you probably agree 
 with Bruce Eckel. If you are a fan of static checking then I 
 don't see how you can like that.
A quote from Uncle Bob about too much static typing and checked exceptions: http://blog.cleancoder.com/uncle-bob/2017/01/11/TheDarkPath.html "My problem is that [Kotlin and Swift] have doubled down on strong static typing. Both seem to be intent on closing every single type hole in their parent languages." "I would not call Java a strongly opinionated language when it comes to static typing. You can create structures in Java that follow the type rules nicely; but you can also violate many of the type rules whenever you want or need to. The language complains a bit when you do; and throws up a few roadblocks; but not so many as to be obstructionist. Swift and Kotlin, on the other hand, are completely inflexible when it comes to their type rules. For example, in Swift, if you declare a function to throw an exception, then by God every call to that function, all the way up the stack, must be adorned with a do-try block, or a try!, or a try?. There is no way, in this language, to silently throw an exception all the way to the top level; without paving a super-hiway for it up through the entire calling tree."
Jun 27
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Tuesday, 27 June 2017 at 16:54:08 UTC, Sebastien Alaiwan wrote:
 adorned with a do-try block, or a try!, or a try?. There is no 
 way, in this language, to silently throw an exception all the 
 way to the top level; without paving a super-hiway for it up 
 through the entire calling tree."
I don't think this is the best argument, if the programmer wants to reduce the number of exceptions that are propagated then the exception should be recast to a more abstract exception between module boundaries. So the "super highway" issue is more a result of the programmer not being interested in structuring error-handling, but rather view errors as something that never should happen. Which may make sense in some applications, but not in all.
Jun 28
prev sibling next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Tuesday, 27 June 2017 at 06:10:52 UTC, Tobias MĂĽller wrote:
 Moritz Maxeiner <moritz ucworks.org> wrote:
 [...]
 Or, more succinct: You must either manually write things down 
 the
 compiler could find out in a fraction of the time via static
 analysis, or cheat the system; both cases are bad code.
It's not at all bad code to write things down that the compiler could infer, quite the opposite.
Of course it is bad, because the compiler can do it better (no chance for a wrong exception set sans compiler bugs) and faster than you.
 Writing it down signals _intent_ and the compiler can check if 
 the implementation is matching the specification
Verifying that a function meets its specification is what unittests are for (asserts for runtime behaviour, static asserts for types). With a function set trait there's nothing stopping you from writing a template that allows you to do the following: --- void foo() { ... } unittest { static assert (throws!(foo, AException)); static assert (throwsAll!(foo, AException, BException)); static assert (throwsAny!(foo, AException, CException)); static assert (!throws!(foo, CException)); } --- which would be idiomatic D, giving you not only the same guarantees as checked exceptions (without the downsides), but also even more versatility by allowing arbitrary checks on the exception set.
 which gives you additional security.
This has nothing to do with security; you may be thinking of safety.
 Additionally it allowes the compiler to do the checks locally 
 which is much
 easier.
Easier to implement in the compiler, yes. That's precisely why I called it a compiler deficiency compared to exposing the function exception set, which is more advanced.
 Function signatures are interfaces which should be 
 self-contained IMO, i.e. it should not be necessary to examine 
 the function body.
 That's what signatures are for. At very least for public 
 interfaces.
Sure, but since a function's signature in D does not include its exception set (be they checked or not), that point is moot.
 I honestly don't understand how people that care a great deal 
 about expressive type systems can be so opposed to checked 
 exceptions. After all they wouldn't use 'object' for everything 
 either.
For exactly the reasons I have already explained.
Jun 27
parent reply mckoder <mckoder yahoo.com> writes:
On Tuesday, 27 June 2017 at 11:40:02 UTC, Moritz Maxeiner wrote:
 It's not at all bad code to write things down that the 
 compiler could infer, quite the opposite.
Of course it is bad, because the compiler can do it better (no chance for a wrong exception set sans compiler bugs) and faster than you.
 Writing it down signals _intent_ and the compiler can check if 
 the implementation is matching the specification
Verifying that a function meets its specification is what unittests are for (asserts for runtime behaviour, static asserts for types).
You might as well argue that you shouldn't have to declare the return type of functions because the compiler can determine that automatically based on the types of values you are actually returning. Then write unit tests to make sure that the return type as determined by the compiler matches what you intended.
Jun 27
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Tuesday, 27 June 2017 at 16:01:37 UTC, mckoder wrote:
 On Tuesday, 27 June 2017 at 11:40:02 UTC, Moritz Maxeiner wrote:
 It's not at all bad code to write things down that the 
 compiler could infer, quite the opposite.
Of course it is bad, because the compiler can do it better (no chance for a wrong exception set sans compiler bugs) and faster than you.
 Writing it down signals _intent_ and the compiler can check 
 if the implementation is matching the specification
Verifying that a function meets its specification is what unittests are for (asserts for runtime behaviour, static asserts for types).
You might as well argue that you shouldn't have to declare the return type of functions because the compiler can determine that automatically based on the types of values you are actually returning.
Of *course* you shouldn't have to (-> auto functions [1]). The difference being, of course, that specifying the return type is always only a single type, whereas specifying the exception set blows up in verbosity with each additional level of call hierarchy.
 Then write unit tests to make sure that the return type as 
 determined by the compiler matches what you intended.
If you don't trust the compiler's return type inference you can do that, of course. Though in contrast to the exception set there's a lot less reason to do so in the return type case, because it's only a single type not an arbitrarily large set of types. [1] https://dlang.org/spec/function.html#auto-functions
Jun 27
parent reply jag <jagseattle hotmail.com> writes:
As Tobias mentioned, there are safety implications to "auto" 
behavior. In the example below I am using C# and its "var" 
feature:

class A {
    public static Employee getFoo() {
       return getPoorPerformingEmployee();
    }
}

This is your code, in which you are calling A which was written 
by someone else:

class B {
    var foo = A.getFoo();
    foo.fire();
}

Now another programmer changes class A as follows:

class A {
    public static Missile getFoo() {
       return new Missile();
    }
}

Guess what happens now? You intended to fire an employee, but 
instead you have fired a missile. The compiler did not catch this 
grave mistake because you did not clearly state your intent. Your 
class B compiles fine because both Employee and Missile have a 
method named fire(). You could have expressed your intent more 
clearly like this:

class B {
    Employee foo = A.getFoo();
    foo.fire();
}

Now the compiler is able to catch your mistake. This is the 
reason your code becomes safer when you express your intent 
clearly. You should have the option to state your intent clearly 
by listing the exceptions that can be thrown by a method.

In dynamic languages like Python there is less ability to state 
intent clearly. As a result in such languages fewer bugs can be 
caught at compile time. More bugs show up at run time. This may 
be acceptable if the program is intended for internal use in a 
company, because when the program crashes they can call you to 
come and fix it because you are in the next office. But when 
reliability is important then the more opportunity there is to 
express intent and have the compiler verify your code against 
your intent, the better.
Jun 27
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Tuesday, 27 June 2017 at 18:14:47 UTC, jag wrote:
 As Tobias mentioned, there are safety implications to "auto" 
 behavior. In the example below I am using C# and its "var" 
 feature:

 [...]
Your example uses variable type inference, which happens on the *caller* side, *not* the *callee* side, the latter of which being where both checked exceptions and return type inference happen. The correct analogue to your example in the exception domain is thus *not* checked exceptions, but verifying the exception set *at the call site* (the same way you fix the variable type *at the call site* to `Employee`), which is exactly what you can do by exposing the function exception set via a trait.
 Now the compiler is able to catch your mistake. This is the 
 reason your code becomes safer when you express your intent 
 clearly. You should have the option to state your intent 
 clearly by listing the exceptions that can be thrown by a 
 method.
As I have pointed out, your example occurs on the *caller* side, not the *callee* side. The proper solution is not for the callee to specify which exceptions it may throw, but for the caller to specify which exceptions it allows the callee to throw (using compile time introspection on the exception set).
 In dynamic languages like Python [...]
None of this justifies putting the burden on the callee side (i.e. checked exceptions), instead of on the caller side (the latter being implementable in an idiomatic way using a trait as John has proposed).
Jun 27
parent reply jag <jagseattle hotmail.com> writes:
On Tuesday, 27 June 2017 at 19:37:24 UTC, Moritz Maxeiner wrote:
 As I have pointed out, your example occurs on the *caller* 
 side, not the *callee* side. The proper solution is not for the 
 callee to specify which exceptions it may throw, but for the 
 caller to specify which exceptions it allows the callee to 
 throw (using compile time introspection on the exception set).
Can I as a programmer who wants to call a function written by someone else inspect the declaration of that function and know what exceptions are possible? If no how is this supposed to work? I am supposed to specify that exceptions I want to allow the called function to throw? The called function is not going to dynamically adapt itself and change the list of exceptions it throws, right? So how can I know, before running the code, what exceptions are possible?
Jun 27
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Tuesday, 27 June 2017 at 21:47:49 UTC, jag wrote:
 On Tuesday, 27 June 2017 at 19:37:24 UTC, Moritz Maxeiner wrote:
 As I have pointed out, your example occurs on the *caller* 
 side, not the *callee* side. The proper solution is not for 
 the callee to specify which exceptions it may throw, but for 
 the caller to specify which exceptions it allows the callee to 
 throw (using compile time introspection on the exception set).
Can I as a programmer who wants to call a function written by someone else inspect the declaration of that function and know what exceptions are possible?
* Can I as a programmer who wants to call a function written by someone else inspect that function's exception set? Yes, as explained fully here [1], shown as an idiomatic D trait here [2], and a template based abstraction around that trait here [3]. It would need to be implemented, of course, but it's not conceptually hard.
 If no how is this supposed to work?
It was explained multiple times in this thread.
 I am supposed to specify that exceptions I want to allow the 
 called function to throw?
You aren't supposed to do anything, but you can do that, yes.
 The called function is not going to dynamically adapt itself 
 and change the list of exceptions it throws, right?
What? A function's exception set is determined by its body and can be aggregated by the compiler in a single recursive pass in the static analysis phase.
 So how can I know, before running the code, what exceptions are 
 possible?
You mean the very first time you want to call it and you don't know the exception set yourself by looking at its signature? Put the call in a nothrow scope and compile the module (which is fast in D), the compiler will then complain which exceptions you didn't catch (requires improvement of nothrow analysis [1]). [1] http://forum.dlang.org/post/uovtkvpdagzagzhyacbp forum.dlang.org [2] http://forum.dlang.org/post/lxejskhonjtiifvvgwnd forum.dlang.org [3] http://forum.dlang.org/post/fjjkqbxiznlxfstqntnv forum.dlang.org
Jun 27
parent reply mckoder <mckoder yahoo.com> writes:
On Tuesday, 27 June 2017 at 22:56:47 UTC, Moritz Maxeiner wrote:
 You mean the very first time you want to call it and you don't 
 know the exception set yourself by looking at its signature?
 Put the call in a nothrow scope and compile the module (which 
 is fast in D), the compiler will then complain which exceptions 
 you didn't catch (requires improvement of nothrow analysis [1]).
So to know what exceptions are possible you have to compile the code? I consider that inferior to other solutions such as callee explicitly stating what exceptions it may throw, because then all you have to do is glance at the callee. Also, I think explicitly stating your intent in this manner (by the callee not just the caller) is a good idea, to make sure you are not throwing some exception you didn't mean to throw.
Jun 27
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 28 June 2017 at 01:35:32 UTC, mckoder wrote:
 On Tuesday, 27 June 2017 at 22:56:47 UTC, Moritz Maxeiner wrote:
 You mean the very first time you want to call it and you don't 
 know the exception set yourself by looking at its signature?
 Put the call in a nothrow scope and compile the module (which 
 is fast in D), the compiler will then complain which 
 exceptions you didn't catch (requires improvement of nothrow 
 analysis [1]).
So to know what exceptions are possible you have to compile the code?
That, or do the even faster thing and use what's written in the documentation (which a sensible person might generate automatically, embedding the generated exception set for each function). There are only two outcomes (sans compiler bugs): - What's written there is correct -> It will compile and you're fine - What's written there is incorrect -> It won't compile and tell you what's missing In the 2nd case you have discovered a bug in whatever codebase you are using. Report it.
 I consider that inferior to other solutions such as callee 
 explicitly stating what exceptions it may throw, because then 
 all you have to do is glance at the callee.
On the one hand you have a small one-time cost (I would go as far as calling it tiny, since I consider it a reasonable assumption for you to have the documentation open), on the other hand you have a one-time cost with its size depending on the call hierarchy level and small constant maintenance costs on every change. If you consider the first inferior to the second, that's your call, but I disagree strongly. That being said, nothing prevents you from putting it there anyway: --- static assert (throwsExactly!(foo, AException, BException)); void foo() { ... } ---
 Also, I think  explicitly stating your intent in this manner 
 (by the callee not just the caller) is a good idea, to make 
 sure you are not throwing some exception you didn't mean to 
 throw.
Add a static assert as shown above and you're done.
Jun 27
parent Moritz Maxeiner <moritz ucworks.org> writes:
On Wednesday, 28 June 2017 at 02:09:40 UTC, Moritz Maxeiner wrote:
 ---
 static assert (throwsExactly!(foo, AException, BException));
 void foo() { ... }
 ---
One could even go a bit farther and implement a mixin template using `typeof(this)` and do: --- void foo() { mixin ThrowsExactly!(AException, BException); } ---
Jun 27
prev sibling parent Jesse Phillips <Jesse.K.Phillips+D gmail.com> writes:
On Tuesday, 27 June 2017 at 06:10:52 UTC, Tobias MĂĽller wrote:
 I honestly don't understand how people that care a great deal 
 about expressive type systems can be so opposed to checked 
 exceptions. After all they wouldn't use 'object' for everything 
 either.

 Tobi
I think there is a threshold. For example, personally I am against the compiler refusing to compile because of an unused variable, or import clause. Why, because they get in the way of making changes. I don't want to pacify the compiler as I progress through the development process. Generally when the compiler complains about a type mismatch I actually forgot to make additional changes to make the new thing work. Checked exceptions do the same thing. Call a new function, propagate the exception just to determine if it is a desired change. This very much can lead to attempting to pacify without properly handling. This discussion seems to emphasize the ability to handle every exception type. There are certainly times I've utilized the ability to handle different types of exception in different ways, but it isn't the norm. This is likely because generally I find Exception to crop up because of programming logic bugs rather than true exceptions and in the other cases I'm not using the type of exception to change how I deal with a problem; if my Json parsing throws a Conversion exception I'm not going to handle it differently than when it throws an Invalid Token exception. I however don't have much experience managing checked Exceptions. I remember back in the day trying to fix the long list of exceptions my functions could throw. I'd commonly use throws Exception, pondered on wrapping exceptions into my own exception that would then be thrown. Neither of these would be helpful to the original points, adding exceptions should break the contract and you should be able to catch specific exceptions. How is the ever expanding exception list of static void Main managed by others?
Jun 27
prev sibling parent reply Crayo List <crayolist gmail.com> writes:
On Monday, 26 June 2017 at 15:40:19 UTC, mckoder wrote:


 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.
This statement is logically equivalent to "good code can only be written with checked exceptions (presumably using Java), all other code is bad". Checked exceptions are a horrible idea because they leak internal implementation details as part of the signature of a method directly and in a transitive manner, which of course is one huge aberration!
Jun 29
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Thursday, 29 June 2017 at 19:34:22 UTC, Crayo List wrote:
 Checked exceptions are a horrible idea because they leak 
 internal implementation details as part of the signature of a 
 method directly and in a transitive manner, which of course is 
 one huge aberration!
What do you mean? Exceptions that leave a module clearly aren't internal implementation details…
Jun 29
parent reply Crayo List <crayolist gmail.com> writes:
On Thursday, 29 June 2017 at 21:06:12 UTC, Ola Fosheim Grøstad 
wrote:
 On Thursday, 29 June 2017 at 19:34:22 UTC, Crayo List wrote:
 Checked exceptions are a horrible idea because they leak 
 internal implementation details as part of the signature of a 
 method directly and in a transitive manner, which of course is 
 one huge aberration!
What do you mean? Exceptions that leave a module clearly aren't internal implementation details…
Correct! But I said 'checked exceptions' are.
Jul 03
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 3 July 2017 at 20:22:39 UTC, Crayo List wrote:
 On Thursday, 29 June 2017 at 21:06:12 UTC, Ola Fosheim Grøstad 
 wrote:
 On Thursday, 29 June 2017 at 19:34:22 UTC, Crayo List wrote:
 Checked exceptions are a horrible idea because they leak 
 internal implementation details as part of the signature of a 
 method directly and in a transitive manner, which of course 
 is one huge aberration!
What do you mean? Exceptions that leave a module clearly aren't internal implementation details…
Correct! But I said 'checked exceptions' are.
How does "checked" affect whether something is internal or not?
Jul 04
parent reply Crayo List <crayolist gmail.com> writes:
On Tuesday, 4 July 2017 at 10:14:11 UTC, Ola Fosheim Grøstad 
wrote:
 On Monday, 3 July 2017 at 20:22:39 UTC, Crayo List wrote:
 On Thursday, 29 June 2017 at 21:06:12 UTC, Ola Fosheim Grøstad 
 wrote:
 On Thursday, 29 June 2017 at 19:34:22 UTC, Crayo List wrote:
 Checked exceptions are a horrible idea because they leak 
 internal implementation details as part of the signature of 
 a method directly and in a transitive manner, which of 
 course is one huge aberration!
What do you mean? Exceptions that leave a module clearly aren't internal implementation details…
Correct! But I said 'checked exceptions' are.
How does "checked" affect whether something is internal or not?
int open(string file) { if(!exists(file)) throw new SomeException; ... } a few weeks later; int open(string file) { if(file == null) threw new NullSomethingException; same as before } What happens to the 3000 direct and indirect calls to open() ? Notice how the 'interface' has not changed, only the implementation.
Jul 05
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Wednesday, 5 July 2017 at 15:48:33 UTC, Crayo List wrote:
 What happens to the 3000 direct and indirect calls to open() ?

 Notice how the 'interface' has not changed, only the 
 implementation.
No, the exception spec is part of the interface whether it is in the function declaration or not. Such a change could be fatal in any system. What you should do is to specify what Exceptions are thrown across module borders. That means you should recast the internal exceptions into module-level exceptions.
Jul 05
parent reply Crayo List <crayolist gmail.com> writes:
On Wednesday, 5 July 2017 at 21:05:04 UTC, Ola Fosheim Grøstad 
wrote:
 On Wednesday, 5 July 2017 at 15:48:33 UTC, Crayo List wrote:
 What happens to the 3000 direct and indirect calls to open() ?

 Notice how the 'interface' has not changed, only the 
 implementation.
No, the exception spec is part of the interface whether it is in the function declaration or not.
I disagree! Once an interface is defined you should be able to alter the implementation all you want without impacting client code!
Such a change could be
 fatal in any system.
No idea what you mean here.
 What you should do is to specify what Exceptions are thrown 
 across module borders. That means you should recast the 
 internal exceptions into module-level exceptions.
I don't need to do anything since I don't have a problem. I don't understand what are you helping me with. I pointed out how checked exceptions 'leak' internal implementation, that was all.
Jul 06
parent Biotronic <simen.kjaras gmail.com> writes:
On Thursday, 6 July 2017 at 14:59:10 UTC, Crayo List wrote:
 On Wednesday, 5 July 2017 at 21:05:04 UTC, Ola Fosheim Grøstad 
 wrote:
 On Wednesday, 5 July 2017 at 15:48:33 UTC, Crayo List wrote:
 What happens to the 3000 direct and indirect calls to open() ?

 Notice how the 'interface' has not changed, only the 
 implementation.
No, the exception spec is part of the interface whether it is in the function declaration or not.
I disagree! Once an interface is defined you should be able to alter the implementation all you want without impacting client code!
Correct. And throwing a different kind of exception changes the interface, and *does* impact client code. Consider: void fun() { try { gun(); } catch (GoodException goodEx) { // Ignore } catch (Exception badEx) { FireZeeMissiles(); } } void gun() { throw new GoodException (); } Now J. Random Programmer decides to change gun()'s implementation: void gun() { throw new BadException(); } If your assertion that this doesn't change the interface and doesn't impact the client is correct, no missiles will be fired. Sadly, this is not the case, and Switzerland is now wiped off the map. -- Biotronic
Jul 07
prev sibling next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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?
Jun 26
prev sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
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.
Jun 26
next sibling parent reply Sebastien Alaiwan <ace17 free.fr> writes:
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.
Jun 26
parent reply Guillaume Boucher <guillaume.boucher.d gmail.com> writes:
On Monday, 26 June 2017 at 16:52:22 UTC, Sebastien Alaiwan wrote:
 Checked exceptions allow a lot more precision about what types 
 of exceptions a function can throw.
I totally agree that this is a problem with D right now. If you want to catch all errors, how are you supposed to remember what std.file.readText throws? Or std.file.mkdir? There are two solutions to this problem that I know of: A) Checked exceptions B) Error codes that can't be implicitly ignored Java uses A, Rust/Go use B. C++ uses B to some extend (e.g. in std::experimental::filesystem). In my opinion, option B better than A because checked exceptions are incredibly verbose. However, both are better than nothing (which is the current state of D right now). It is very well possible to use option B in D. The most convenient one is making functions nothrow and use Algebraic!(T, ErrorCode), or, for void functions, have a parameter "ref ErrorCode". If all functions in Phobos would either follow that pattern or provide an alternative nothrow overload, I would consider that problem solved.
Jun 26
next sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
On Monday, 26 June 2017 at 17:44:15 UTC, Guillaume Boucher wrote:
 It is very well possible to use option B in D.  The most 
 convenient one is making functions nothrow and use 
 Algebraic!(T, ErrorCode), or, for void functions, have a 
 parameter "ref ErrorCode".
 If all functions in Phobos would either follow that pattern or 
 provide an alternative nothrow overload, I would consider that 
 problem solved.
I have tried using such Monads in D, but in the end it always ended up being too verbose or too hard to read compared to using exceptions or even simple error codes (with 0 == no error).
Jun 26
parent Guillaume Boucher <guillaume.boucher.d gmail.com> writes:
On Monday, 26 June 2017 at 17:50:47 UTC, Moritz Maxeiner wrote:
 I have tried using such Monads in D, but in the end it always 
 ended up being too verbose or too hard to read compared to 
 using exceptions or even simple error codes (with 0 == no 
 error).
I haven't tried that yet, tbh. visit is nice, but can't always be used. So I guess unless D introduces syntax for pattern matching, it will always be verbose. In that case a reference to an error code would be the most viable design in D.
Jun 26
prev sibling next sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 26 June 2017 at 17:44:15 UTC, Guillaume Boucher wrote:
 Java uses A, Rust/Go use B.  C++ uses B to some extend (e.g. in 
 std::experimental::filesystem).
The C++17 filesystem api provides two alternatives, the standard filesystem_error exception and an output-paramater for capturing os-specific error codes. I'm not quite sure why they provide both, but I guess performance and the ability to compile for runtimes with exceptions turned off could explain it. It is rather clear though that C++ std lib relies heavily on exceptions.
Jun 26
parent reply Guillaume Boucher <guillaume.boucher.d gmail.com> writes:
On Monday, 26 June 2017 at 18:42:24 UTC, Ola Fosheim Grøstad 
wrote:
 On Monday, 26 June 2017 at 17:44:15 UTC, Guillaume Boucher 
 wrote:
 Java uses A, Rust/Go use B.  C++ uses B to some extend (e.g. 
 in std::experimental::filesystem).
The C++17 filesystem api provides two alternatives, the standard filesystem_error exception and an output-paramater for capturing os-specific error codes. I'm not quite sure why they provide both, but I guess performance and the ability to compile for runtimes with exceptions turned off could explain it.
Quoting the C++ standard:
 Filesystem library functions often provide two overloads, one 
 that
 throws an exception to report file system errors, and another 
 that sets an error_code.
 
 [Note: This supports two common use cases:

    - Uses where file system errors are truly exceptional
      and indicate a serious failure.
      Throwing an exception is an appropriate response.
    - Uses where file system errors are routine
      and do not necessarily represent failure.
      Returning an error code is the most appropriate response.
      This allows application specific error handling, including 
 simply ignoring the error.

  -- end note]
I would say that the overload without exceptions is the "standard" one.
 It is rather clear though that C++ std lib relies heavily on 
 exceptions.
[Citation needed]
Jun 26
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 26 June 2017 at 21:00:24 UTC, Guillaume Boucher wrote:
 I would say that the overload without exceptions is the 
 "standard" one.
The C++ assumption is that exceptions are slow. So what the text you referenced says is that it provides an alternative mechanism for situations where you are testing for failure, e.g. accessing something that isn't present to see if it exists. It is a performance/convenience alternative. Although that "nothrow design" is rather clumsy and inconvenient since you have to provide the error object yourself as a parameter.
 It is rather clear though that C++ std lib relies heavily on 
 exceptions.
[Citation needed]
No citation needed. RAII + exceptions has been extensively described by Stroustrup as a main code structuring mechanism for C++ since the 1980s. Basic data structures like std::vector is designed with that in mind. C++ programmers that turn off exceptions also have to be careful with many areas of C++ std::lib.
Jun 26
prev sibling parent Sebastien Alaiwan <ace17 free.fr> writes:
On Monday, 26 June 2017 at 17:44:15 UTC, Guillaume Boucher wrote:
 On Monday, 26 June 2017 at 16:52:22 UTC, Sebastien Alaiwan 
 wrote:
 Checked exceptions allow a lot more precision about what types 
 of exceptions a function can throw.
I totally agree that this is a problem with D right now.
This wasn't my point! I don't think there's a problem with D not having CE. I was just pointing out the difference between "nothrow" specifications and CE.
Jun 26
prev sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
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.
Jun 26
prev sibling next sibling parent reply John Colvin <john.loughran.colvin gmail.com> writes:
On Sunday, 25 June 2017 at 17:38:14 UTC, mckoder wrote:
 I am disappointed that D doesn't have checked exceptions.
I wonder what could be done with something like this: void foo(int a) { if (a > 0) throw new BlahException("blah"); throw new BloopException("bloop"); } unittest { // NEW FEATURE HERE alias Exceptions = __traits(thrownTypes, foo); static assert (staticIndexOf!(BlahException, Exceptions) >= 0); static assert (staticIndexOf!(BloopException, Exceptions) >= 0); } I'm imagining one could use that to do quite a lot of what checked exceptions provide.
Jun 26
next sibling parent Moritz Maxeiner <moritz ucworks.org> writes:
On Monday, 26 June 2017 at 21:53:57 UTC, John Colvin wrote:
 On Sunday, 25 June 2017 at 17:38:14 UTC, mckoder wrote:
 I am disappointed that D doesn't have checked exceptions.
I wonder what could be done with something like this: void foo(int a) { if (a > 0) throw new BlahException("blah"); throw new BloopException("bloop"); } unittest { // NEW FEATURE HERE alias Exceptions = __traits(thrownTypes, foo); } I'm imagining one could use that to do quite a lot of what checked exceptions provide.
Yes, that would be a D idiomatic implementation of the function exception set I mentioned [1]. [1] http://forum.dlang.org/post/uovtkvpdagzagzhyacbp forum.dlang.org
Jun 26
prev sibling parent reply jag <jagseattle hotmail.com> writes:
On Monday, 26 June 2017 at 21:53:57 UTC, John Colvin wrote:
 I wonder what could be done with something like this:

 void foo(int a)
 {
     if (a > 0)
         throw new BlahException("blah");
     throw new BloopException("bloop");
 }

 unittest
 {
     // NEW FEATURE HERE
     alias Exceptions = __traits(thrownTypes, foo);
     static assert (staticIndexOf!(BlahException, Exceptions) >= 
 0);
     static assert (staticIndexOf!(BloopException, Exceptions)
= 0);
} I'm imagining one could use that to do quite a lot of what checked exceptions provide.
So the only way for a programmer to know what exceptions can be thrown by a method is by running the code? In Java this is known while you are writing the code, even before you compile the code. And the compiler verifies that you are handling or passing on all possible exceptions. This is important.
Jun 26
next sibling parent Moritz Maxeiner <moritz ucworks.org> writes:
On Tuesday, 27 June 2017 at 00:10:32 UTC, jag wrote:
 On Monday, 26 June 2017 at 21:53:57 UTC, John Colvin wrote:
 I wonder what could be done with something like this:

 void foo(int a)
 {
     if (a > 0)
         throw new BlahException("blah");
     throw new BloopException("bloop");
 }

 unittest
 {
     // NEW FEATURE HERE
     alias Exceptions = __traits(thrownTypes, foo);
     static assert (staticIndexOf!(BlahException, Exceptions)
= 0);
static assert (staticIndexOf!(BloopException, Exceptions)
= 0);
} I'm imagining one could use that to do quite a lot of what checked exceptions provide.
So the only way for a programmer to know what exceptions can be thrown by a method is by running the code? In Java this is known while you are writing the code, even before you compile the code. And the compiler verifies that you are handling or passing on all possible exceptions. This is important.
As I have pointed out, implementing such a feature for function exception sets would be *one* component; the others are mentioned here [1]. [1] http://forum.dlang.org/post/uovtkvpdagzagzhyacbp forum.dlang.org
Jun 26
prev sibling parent John Colvin <john.loughran.colvin gmail.com> writes:
On Tuesday, 27 June 2017 at 00:10:32 UTC, jag wrote:
 On Monday, 26 June 2017 at 21:53:57 UTC, John Colvin wrote:
 I wonder what could be done with something like this:

 void foo(int a)
 {
     if (a > 0)
         throw new BlahException("blah");
     throw new BloopException("bloop");
 }

 unittest
 {
     // NEW FEATURE HERE
     alias Exceptions = __traits(thrownTypes, foo);
     static assert (staticIndexOf!(BlahException, Exceptions)
= 0);
static assert (staticIndexOf!(BloopException, Exceptions)
= 0);
} I'm imagining one could use that to do quite a lot of what checked exceptions provide.
So the only way for a programmer to know what exceptions can be thrown by a method is by running the code? In Java this is known while you are writing the code, even before you compile the code. And the compiler verifies that you are handling or passing on all possible exceptions. This is important.
No, that's completely the opposite of what I was suggesting. It would all be static (i.e. compile-time) introspection.
Jun 27
prev sibling next sibling parent crimaniak <crimaniak gmail.com> writes:
On Sunday, 25 June 2017 at 17:38:14 UTC, mckoder wrote:
 I am disappointed that D doesn't have checked exceptions.
Warning, Google translate is used! (sorry) I fully support mckoder with regard to exceptions. This is a great advantage of Java. I think, the problem with the introduction of verified exceptions in D is rather psychological, as the authors and the community mostly came from C++. Indeed, in C++, the checked exceptions failed, but I think this is more a failure of C++ than the ideas of checked exceptions. The Java experience has shown that this is a powerful tool that really helps to write reliable programs. Most of the arguments against exceptions are somewhat similar to the arguments of language lovers with a weak dynamic typing, by which listing argument types seems tedious. The relevant objection is that there is a problem with lambdas. I think everyone who started using the stream API in Java 8 was faced with this problem. But who said that you need to exactly repeat the approach of Java? When I encountered this problem, I tried to write an analog of this API, only with support for exceptions. As a result, the root of the problem was easily identified. In Java, each exception type thrown is an additional method parameter, similar to the input parameters, and there are no variadic templates for them. The correct implementation should have some TypeTuple for all the types thrown. And the possibility of calculating it. The following thoughts should be considered as speculative, I understand that these ideas will not be accepted. Take for start the system adopted in Java. Now add 'throws auto'. This does not mean 'throws Exception'! This means that the compiler must determine what exceptions this method or function throws. Now add a default - all functions where there are no throws are treated like 'throws auto'. What does this give us? The old code without throws continues to work, since exceptions, except for intercepted ones, successfully pop up to main() and are caught by runtime. But as soon as the author began to denote throws or nothrows, he gets all the benefits of the Java exception system. In addition, if the lower layer has changed, the intermediate layers that delegate the exceptions above do not need to be rewritten, only the code that deals directly with the exception processing and declares it with throws or nothrows will be affected. (I think that you should not abuse this, libraries that are supplied as a separate product should still encapsulate the underlying exceptions in their own). In addition, now the specification of 'throws A, B, C' lowered to 'throws ExceptionTypeTyple! (A, B, C)'. What does it give? We easily write templates that work with functions with any number of types of throws exceptions, and this can be handled according to the same rules as the other template parameters of the method.
Jun 26
prev sibling next sibling parent Gary Willoughby <dev nomad.so> writes:
I think it's important to understand, D is *not* Java.
Jun 27
prev sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 06/25/2017 01:38 PM, mckoder wrote:
 I am disappointed that D doesn't have checked exceptions.
 
Huh? Is it April 1st?
Jun 28
parent Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Wednesday, June 28, 2017 21:47:56 Nick Sabalausky  via Digitalmars-d 
wrote:
 On 06/25/2017 01:38 PM, mckoder wrote:
 I am disappointed that D doesn't have checked exceptions.
Huh? Is it April 1st?
That was kind of my reaction. It has been my impression that while many folks initially seem to think that checked exceptions are a good idea, it has generally been accepted by the programming community at large that they are in fact a bad idea. AFAIK, Java is the only language to have checked exceptions, and every language that has come after has avoided them. Regardless, based on what Walter has said previously, I think that it's quite clear that there is zero danger of checked exceptions ever being added to D and thus there is really no need to spend time or energy arguing about it, which is part of why I've essentially ignored this thread (and is probably why a lot of major posters have ignored it). I have too little time to spend on D-related things right now as it is. LOL. On that count, I probably shouldn't even have spent the time to reply to this... - Jonathan M Davis
Jul 03