Jump to page: 1 24  
Page
Thread overview
Praise, Contract Programming, and Exceptions
Aug 25, 2001
Eric Gerlach
Aug 25, 2001
Eric Gerlach
Aug 26, 2001
Bradeeoh
Aug 26, 2001
Dan Hursh
Aug 26, 2001
Eric Gerlach
Nov 04, 2001
Sean L. Palmer
Aug 26, 2001
Walter
Aug 27, 2001
Eric Gerlach
Aug 27, 2001
Walter
Nov 04, 2001
Sean L. Palmer
Aug 28, 2001
Eric Gerlach
Aug 28, 2001
Dan Hursh
Aug 28, 2001
Chris Friesen
Aug 28, 2001
Eric Gerlach
Aug 28, 2001
Dan Hursh
Aug 29, 2001
Eric Gerlach
Compiler warnings
Aug 28, 2001
Walter
Aug 29, 2001
Eric Gerlach
Aug 29, 2001
Walter
Aug 30, 2001
Eric Gerlach
Sep 01, 2001
Walter
Aug 30, 2001
Dan Hursh
Sep 01, 2001
Walter
Sep 03, 2001
Angus Graham
Aug 29, 2001
John Carney
Aug 27, 2001
Dan Hursh
Aug 28, 2001
John Carney
Aug 29, 2001
Dan Hursh
Aug 29, 2001
John Carney
Aug 30, 2001
Dan Hursh
Aug 31, 2001
kaffiene
Aug 31, 2001
Dan Hursh
Aug 31, 2001
kaffiene
Aug 31, 2001
Dan Hursh
Aug 31, 2001
kaffiene
Aug 31, 2001
Russ Lewis
Aug 31, 2001
kaffiene
Aug 29, 2001
Jim Farrand
August 25, 2001
Hello all,

(Author's note: This started out as a much shorter post that was meant to go in another thread... but it kept growing and growing, like Jack's beanstalk.)

First of all, Walter, I'd like to applaud you and your wonderful creation.  D is a language that shows some real promise as an industry standard.  I'm very, very excited about being able to try it eventually.

I think that above and beyond all the other improvements of the language, D's strongest feature is how it encourages robust programming.   Adding contracts (in/out/invariant) as a language construct is a great idea.  All too often a function that someone else writes doesn't do exactly what you thought it would for a boundary case, and having an easy way to know that it's their function and not your code screwing up would be wonderful.  I also love the idea of unit testing built into the classes.  In my experience, it won't get done well otherwise :)

However, I don't think D takes contracts far enough.  I think that all of a function's behaviour should be able to be described in the contract. Specifically, not having a 'throws' clause in a language that *encourages* throwing exceptions is suicide for the robustness that D appears to give.

I'm not going to reiterate all of the reasons to have 'throws' here, because I think they have been well enough stated in the threads: "throws?" and "Exception specification?"  But I am going to say that it is essential to providing the full and complete contract to a function.

When programming by contract, there are always 2 parties to the contract, the user and the implementor.  The implementor sets out a set of requirements and says "If these requirements are fulfilled, then I will do this!" and presents a set of results.  Breaking a contract goes two ways.  If the user does not meet the requirements, the contract is broken, and the implementor is under no obligation to fulfill it. However, if the implementor fails to finish the results, they have broken the contract, and the user will not be happy.

Now, let's take a rather funny hypothetical situation:  Implementors and users (the coders, not the code) will be fined $100 every time they break a contract.  Therefore, both implementors and users will want to keep their contracts all the time to avoid being fined.  But the implementors are in a bind.  Let's say that the 'connect()' function's contract says: "Call me, and I will connect to the server!"  What if the network is down?  It's not the connect() function's fault, but that coder will be fined because he broke the contract with the user.

Exceptions are the way out.  They allow an implementor to say: "Call me, and I will connect to the server EXCEPT if the network is down, or it's Tuesday!"  Now the implementor isn't held liable if the network is down.  Rejoice!  He doesn't get fined!

But without a 'throws' clause in a function declaration, the implementor isn't able to do this.  The 'connect()' function isn't able to give a complete contract to the world on how to use it.

The bottom line is that the 'throws' clause is necessary to provide complete contract programming.  If it isn't going to be in the language, you might as well get rid of all the contract programming constructs (in, out, invariant), as they lose much of their meaning, and only provide weak contracts.

Now, a disclaimer:  This is my only major plea about D.  I love the rest of the language.  If D catches on, it will be a Very Good Thing.

Cheers,

Eric

P.S. I'm going to make another post in a minute about some more ideas for exceptions
P.P.S. After that I'll make a post about a few more small things

August 25, 2001
In anticipation of those that disagree with a semi-mandatory 'throws' clause, I'm going to take some arguments from a "better than C++" exception implementation, Java.

One of the concerns people have with making exceptions mandatory is the need to then either catch every possible exception, or specify it in your throws declaration.  Now while this is true, it's not such a bad thing.  Java deals with it nicely in two ways.

To understand the first method, read section 11.2 (including 11.2.1 and 11.2.2) in the Java Language Speccification to be found at:

http://java.sun.com/docs/books/jls/second_edition/html/exceptions.doc.html#44121

(I strongly encourage reading the entire section 11, it's not that long)

Java allows RuntimeExceptions and Errors not to be declared in the throws, because those ones either are unidentifiable at compile time, or are serious and rare enough to bypass the decalaration rule.  All others must be, and I'd argue should be, based on my previous post.

The second method is documented in Java's documentation on Throwable:

http://java.sun.com/j2se/1.4/docs/api/java/lang/Throwable.html

There's a bit in the middle that talks about 'chaining' of exceptions, which allows an exception to know what exception caused it to be thrown.  Essentially what it allows is this (from the page):

try {
  lowLevelOp();
} catch LowLevelException(le) {
  throw new HighLevelException(le);  // Chaining-aware constructor
}

Then the code catching the HighLevelException(he) can call he.getCause() to get the LowLevelException.  That way, all sorts of low level network exceptions could be abstracted to NetworkException at a higher level with a chain down to the lower-level exception. Anyways, seek out that part and read it.  Good stuff.

I hope this addresses some of the concers that people have had with this clause in previous posts.

Cheers,

Eric

August 26, 2001
I've been doing an INCREDIBLY large amount of Java programming lately, and I just want to say that I knew this, and because of my experience with it, know how invaluabl it is.  I want to second absolutely every thing you said in your first post and the follow up and say that, indeed, the throws clause is completely neccesary for real, complete contracted programming (or some substitution thereof) and is even fairly critical outside of contracts (ie, Java)

Of the many reasons programming in Java can go so much quicker and smoother, I think the throws concept requiring Exceptions to be caught is one of them, because the compiler KNOWS there may be an exception, and it doesn't let you code without catching the exception and findout later after 2 days of debugging it was that damned Exception all along when, if you were forced to catch it, you would've figured it out in 3 seconds after trying to compile your code without it.

<phew>

Anyway, long story short - throws = important and required, please include. :)

-Brady


"Eric Gerlach" <egerlach@canada.com> wrote in message news:3B883583.1020802@canada.com...
> In anticipation of those that disagree with a semi-mandatory 'throws' clause, I'm going to take some arguments from a "better than C++" exception implementation, Java.
>
> One of the concerns people have with making exceptions mandatory is the need to then either catch every possible exception, or specify it in your throws declaration.  Now while this is true, it's not such a bad thing.  Java deals with it nicely in two ways.
>
> To understand the first method, read section 11.2 (including 11.2.1 and 11.2.2) in the Java Language Speccification to be found at:
>
>
http://java.sun.com/docs/books/jls/second_edition/html/exceptions.doc.html#4 4121
>
> (I strongly encourage reading the entire section 11, it's not that long)
>
> Java allows RuntimeExceptions and Errors not to be declared in the throws, because those ones either are unidentifiable at compile time, or are serious and rare enough to bypass the decalaration rule.  All others must be, and I'd argue should be, based on my previous post.
>
> The second method is documented in Java's documentation on Throwable:
>
> http://java.sun.com/j2se/1.4/docs/api/java/lang/Throwable.html
>
> There's a bit in the middle that talks about 'chaining' of exceptions,
> which allows an exception to know what exception caused it to be thrown.
>   Essentially what it allows is this (from the page):
>
> try {
>    lowLevelOp();
> } catch LowLevelException(le) {
>    throw new HighLevelException(le);  // Chaining-aware constructor
> }
>
> Then the code catching the HighLevelException(he) can call he.getCause() to get the LowLevelException.  That way, all sorts of low level network exceptions could be abstracted to NetworkException at a higher level with a chain down to the lower-level exception. Anyways, seek out that part and read it.  Good stuff.
>
> I hope this addresses some of the concers that people have had with this clause in previous posts.
>
> Cheers,
>
> Eric
>


August 26, 2001
	Well, I don't want to say anything nice about java but I would like to
re-second this.  I would like to be able to look at a function and have
a reasonable idea of the error I need to be ready to handle.
	I wouldn't against having the default behavior (when there is no throws
clause) be that it can throw anything, so only the masochists need
declare what they are doing.  I do believe that any library that wants
to be taken seriously should, by convention, declare throws clauses for
every method.  This way folks who like to try to recover or clean up can
have some idea what can go wrong.  Otherwise I don't see how you could
do anything more that say
	"I died because " + exception.msg();

Dan

Bradeeoh wrote:
> 
> I've been doing an INCREDIBLY large amount of Java programming lately, and I just want to say that I knew this, and because of my experience with it, know how invaluabl it is.  I want to second absolutely every thing you said in your first post and the follow up and say that, indeed, the throws clause is completely neccesary for real, complete contracted programming (or some substitution thereof) and is even fairly critical outside of contracts (ie, Java)
> 
> Of the many reasons programming in Java can go so much quicker and smoother, I think the throws concept requiring Exceptions to be caught is one of them, because the compiler KNOWS there may be an exception, and it doesn't let you code without catching the exception and findout later after 2 days of debugging it was that damned Exception all along when, if you were forced to catch it, you would've figured it out in 3 seconds after trying to compile your code without it.
> 
> <phew>
> 
> Anyway, long story short - throws = important and required, please include. :)
> 
> -Brady
> 
> "Eric Gerlach" <egerlach@canada.com> wrote in message news:3B883583.1020802@canada.com...
> > In anticipation of those that disagree with a semi-mandatory 'throws' clause, I'm going to take some arguments from a "better than C++" exception implementation, Java.
> >
> > One of the concerns people have with making exceptions mandatory is the need to then either catch every possible exception, or specify it in your throws declaration.  Now while this is true, it's not such a bad thing.  Java deals with it nicely in two ways.
> >
> > To understand the first method, read section 11.2 (including 11.2.1 and 11.2.2) in the Java Language Speccification to be found at:
> >
> >
> http://java.sun.com/docs/books/jls/second_edition/html/exceptions.doc.html#4 4121
> >
> > (I strongly encourage reading the entire section 11, it's not that long)
> >
> > Java allows RuntimeExceptions and Errors not to be declared in the throws, because those ones either are unidentifiable at compile time, or are serious and rare enough to bypass the decalaration rule.  All others must be, and I'd argue should be, based on my previous post.
> >
> > The second method is documented in Java's documentation on Throwable:
> >
> > http://java.sun.com/j2se/1.4/docs/api/java/lang/Throwable.html
> >
> > There's a bit in the middle that talks about 'chaining' of exceptions,
> > which allows an exception to know what exception caused it to be thrown.
> >   Essentially what it allows is this (from the page):
> >
> > try {
> >    lowLevelOp();
> > } catch LowLevelException(le) {
> >    throw new HighLevelException(le);  // Chaining-aware constructor
> > }
> >
> > Then the code catching the HighLevelException(he) can call he.getCause() to get the LowLevelException.  That way, all sorts of low level network exceptions could be abstracted to NetworkException at a higher level with a chain down to the lower-level exception. Anyways, seek out that part and read it.  Good stuff.
> >
> > I hope this addresses some of the concers that people have had with this clause in previous posts.
> >
> > Cheers,
> >
> > Eric
> >
August 26, 2001
>  Well, I don't want to say anything nice about java but I would like to
> re-second this.  I would like to be able to look at a function and have
> a reasonable idea of the error I need to be ready to handle.

I don't usually advocate Java either... I'm only half-pleased with their VM way of doing things, and am still not sure about the class library, it's complete, but as someonw mentioned, if you use one bit, you have to use all the bits.  However, IMHO exception handling is one thing they got right.

> 	I wouldn't against having the default behavior (when there is no throws
> clause) be that it can throw anything, so only the masochists need
> declare what they are doing.  I do believe that any library that wants
> to be taken seriously should, by convention, declare throws clauses for
> every method.  This way folks who like to try to recover or clean up can
> have some idea what can go wrong.  Otherwise I don't see how you could
> do anything more that say 	"I died because " + exception.msg();

Six months ago I would have been okay with it too, but working on the most recent project I've been working on, I can't say I'm okay with it anymore.  The project was built in C++ through a series of grafts, and eventually emerged as a complete system.

One of the annoying problems we've been having is that occasionally someone forgets to document an exception in a header file.  The exceptional case occurs, and it throws.  That exception propagates half-way up the call stack, and is caught in a really wierd place.  Our exceptions don't know where they came from in code (another problem), and so you spend a while trying to figure out what part of your code threw that exception, and it ends up being annoying.

There are three ways to handle a mandatory throws clause:
1) Don't throw exceptions :)
2) Document every specific exception that could be thrown (the Good Way) or
3) 'throws (Exception)' (the 'I'm Lazy') way.

So, there is a way to get around a mandatory throws clause if you don't want to document every exception.  At least that way people who call your function know you might throw *something*, but it won't be very meaningful.  But, the whole point of meaningful exceptions is so that you can recover gracefully from them.  Otherwise, you're right, all you get is:

"I died because " + exception.msg();

Ow.  I don't want my app doing that.

Eric

P.S. Idea: Exceptions should know what file and line they were thrown from by default.**

August 26, 2001
Here's my problem with a throws clause:

A calls B
B calls C
C calls D

D is in some third party library. Now, D is updated to throw another exception. Now, the code in C, B, and A all needs to be updated. To me, this breaks code reuse.

-Walter


August 27, 2001
Walter wrote:
> Here's my problem with a throws clause:
> 
> A calls B
> B calls C
> C calls D
> 
> D is in some third party library. Now, D is updated to throw another
> exception. Now, the code in C, B, and A all needs to be updated. To me, this
> breaks code reuse.
> 
> -Walter

If you think about it, either way it breaks code reuse.  In fact, changing *any* part of a contract breaks code reuse!  If D changes his pre- or post-conditions, C is going to run into problems, because the 'assert(i<=5)' just became 'assert(i<5)', and he's counting on passing 5s!  That isn't any reason to not include them in the language (as the language D so aptly demonstrates).

Back to exceptions:  If D adds a new exception, C, B, and A are broken anyways, because they don't know it's coming!  I think having the clause breaks code reuse *less*, not more.

Consider a stack of fixed size which used to perform a no-op if you tried to push beyond the limits of the stack.  Well, the author decided that it wasn't good behaviour for the stack, and so now the push() method throws an exception.  The stack is D.

The author of C downloads the new version of D to run his unit tests (yay for unit tests) to make sure his still works.  Now, one of several things happen based on whether there is a mandatory throws clause or not.

Mandatory throws clause:  C's code doesn't compile.  "Damn," he says, and adds the five or so lines of code to catch and chain the exception.  If he's not sure how to handle it, he has to think about it.

No throws clause:  Turns out C's unit tests weren't quite thorough, and he missed the boundary case where the exception would be thrown (C isn't a simple system).

I'm the author of A.

If there is a mandatory throws clause, I probably don't even notice that a new exception is there.  A good programmer at the C or B level will catch and *handle* the exception as it should be handled.  So with a throws clause I'm less likely to be affected at all.  If I am affected, it will be that my code doesn't compile.  No problem.  I add a few lines and go merrily on my way.

That is certainly preferable to the alternative, which occurs without a throws clause.  I find out four months after deployment of my new software that the stupid author of D added an exception to his code which bubbled up to my code.  Now I have to issue a patch and change all my code... But wait!  I can't even catch the specific exception, because it's not in my scope!  In fact, even if I catch a generic Exception, I can't handle it properly, because I have no idea what the innards of B, C, or D are!  Even worse, I don't even know that it is D that is causing me problems!!!  There is no way to fix this problem, except to contact the author of B, who after hours of searching finds the obscure case and contacts the author of C, who does the same and eventually the author of D is contacted who tells us all: "Oh yeah, I added an exception."

What pisses me off more?  Having to add three lines to catch a new exception from B because my code won't compile? (Which likely wouldn't happen) OR the horrible scenario I described in the last paragraph?  I'd say I'd rather recompile than face that crap.

My code is less *critially* broken if the throws clause is there, and mandatory.

Now I can understand your reluctance to add it to the language if there is a good *compiler* reason to leave it out.  But I think from a language architecture standpoint it's a good idea.

Cheers,

Eric

August 27, 2001
Walter wrote:
> 
> Here's my problem with a throws clause:
> 
> A calls B
> B calls C
> C calls D
> 
> D is in some third party library. Now, D is updated to throw another exception. Now, the code in C, B, and A all needs to be updated. To me, this breaks code reuse.
> 
> -Walter

	You are right, but I consider it a side affect of the interface of D
changing.  I be upset if the number or types of parameter to D changed
and the compiler didn't flag that.  Throwing a new type of exception is
like changing the type of a parameter.  One of my big problems with
errno is that platforms can make up their own values.  They can change
them add them remove and I would never know and hence I cannot even
attempt to handle in any way except to ignore them or throw my hands up
and abort.
	So you right, it a pain, but it's D's fault for changing the API.
Error conditions should be a part of a good API.  If A want's he can be
lazy and just have a throws clause of Exception or what ever the
exception base class is.  You could also pick up the syntax:

	void f() throws();		// I throw nothing
	void g() throws(X, Y, Z)	// I throw X, Y & Z type exceptions
	void h();			// I'm making no promises

I think libraries should document their errors with throws clauses, but I wouldn't want the language to enforce that.

Dan
August 27, 2001

Eric Gerlach wrote:
> 
> Walter wrote:
> > Here's my problem with a throws clause:
> >
> > A calls B
> > B calls C
> > C calls D
> >
> > D is in some third party library. Now, D is updated to throw another exception. Now, the code in C, B, and A all needs to be updated. To me, this breaks code reuse.
> >
> > -Walter
> 
> If you think about it, either way it breaks code reuse [...snippy...]

Eric, thanks for your excellent example here. I'd been leaning against throws-contracting out of laziness, and your argument is persuasive.

(Hey, what are the interactions between C++ exceptions and D exceptions? Does propagation always stop at the language barrier?)

I'd personally like to write code for quick-n-dirty tools without being concerned with exceptions at all. Can we define some solution wherein the default behavior is to not care what exceptions might be thrown?

-RB
August 27, 2001
Russell Bornschlegel wrote in message <3B89ECDD.1282D39D@estarcion.com>...
>(Hey, what are the interactions between C++ exceptions and D exceptions? Does propagation always stop at the language barrier?)


Since, in C++, you can throw any type, but in D, you can only throw class instances of a type that doesn't exist in C++, there's an obvious compatibility problem. One solution is to fake a wrapper around any incoming C++ exception, but I feel a better solution is to ignore the problem and assert that exceptions should not propagate across DLL or language boundaries.


>I'd personally like to write code for quick-n-dirty tools without being concerned with exceptions at all. Can we define some solution wherein the default behavior is to not care what exceptions might be thrown?


That might be best.


« First   ‹ Prev
1 2 3 4