www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Exception hierarchy refactoring

reply "Matthew" <admin.hat stlsoft.dot.org> writes:
I've persuaded big-W that the exception hierarchy refactoring can wait no
longer, but to do so I've had to volunteer to 
marshal the efforts.

My plan is as follows:

1. Solicit *on-topic* criticisms from the newsgroup

2. Put on my code reviewer's hat, and do a detailed analysis and criticism of
the current situation. I'll do this 
*before* I read any of these criticisms, so as to be unbiased in the review.
This means you've a week or so before I'll 
be digesting and collating any responses from the community, so please take
your time to provide considered comments.

3. Amalgamate these two sets of information (along with any prior posts on the
subject that I can easily find in the ng) 
into a (hopefully) simple recommendation for necessary changes to Walter. (If
it doesn't fit into three pages (+ 
listings) or less, I shall consider it a failure.)

4. Release the findings, and Walter's response (if it counters any of the
recommendations), to the community for 
*on-topic* debate.

5. Implement any changes, gratefully taking advantage of any volunteers who
popped up their hands in steps 1. or 4..

Note: please resist the temptation to wander OT, or get in slagging matches.
I'm barely less busy than Walter at the 
moment, so am just going to ignore any posts (and their sub-threads) like that,
and any useful criticisms contained 
therein will be wasted.

Hopefully, by the time we hit 0.103 we should be in good shape. If the process
works, we might start a trend.

Cheers

Matthew
Sep 09 2004
next sibling parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:

names:

class Throwable {
 this(char[] msg = null, Throwable cause = null) {...}
 Throwable cause() { getter for cause }
 char[] toString() {...}
 void print() {...} // should not allocate any memory
}

class Error : Throwable {...} // non-recoverable error
class Exception : Throwable {...} // recoverable error

The only Error subclasses should be AssertError, SwitchError. All subclasses
of Exception should have names that end with Exception.


"Matthew" <admin.hat stlsoft.dot.org> wrote in message
news:chrebn$12f7$1 digitaldaemon.com...
 I've persuaded big-W that the exception hierarchy refactoring can wait no
longer, but to do so I've had to volunteer to
 marshal the efforts.

 My plan is as follows:

 1. Solicit *on-topic* criticisms from the newsgroup

 2. Put on my code reviewer's hat, and do a detailed analysis and criticism
of the current situation. I'll do this
 *before* I read any of these criticisms, so as to be unbiased in the
review. This means you've a week or so before I'll
 be digesting and collating any responses from the community, so please
take your time to provide considered comments.
 3. Amalgamate these two sets of information (along with any prior posts on
the subject that I can easily find in the ng)
 into a (hopefully) simple recommendation for necessary changes to Walter.
(If it doesn't fit into three pages (+
 listings) or less, I shall consider it a failure.)

 4. Release the findings, and Walter's response (if it counters any of the
recommendations), to the community for
 *on-topic* debate.

 5. Implement any changes, gratefully taking advantage of any volunteers
who popped up their hands in steps 1. or 4..
 Note: please resist the temptation to wander OT, or get in slagging
matches. I'm barely less busy than Walter at the
 moment, so am just going to ignore any posts (and their sub-threads) like
that, and any useful criticisms contained
 therein will be wasted.

 Hopefully, by the time we hit 0.103 we should be in good shape. If the
process works, we might start a trend.
 Cheers

 Matthew
Sep 10 2004
parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
Let me add I'd like to see some standard exceptions, too. For example there
should be a module called something like std.exceptions that at least

ArgumentException
OutOfMemoryException
NotSupportedException
NotImplementedException
IndexOutOfBoundsException

I suggest this because I have several libraries that use these exceptions
(except OutOfMemory, actually) and writing my own for each library is
annoying and error-prone. For example if I want to perform some argument
checking on an input to a function and throw an Exception if the argument is
illegal then I could write

import std.exceptions;
...
void foo(int must_be_positive) {
  if (must_be_positive <= 0)
    throw new ArgumentException("must_be_positive must be positive");
  ...
}

Plus having a standard "index out of bounds" exception comes in handy with
user-defined container classes.

-Ben

"Ben Hinkle" <bhinkle mathworks.com> wrote in message
news:chsbiq$1hdf$1 digitaldaemon.com...

 names:

 class Throwable {
  this(char[] msg = null, Throwable cause = null) {...}
  Throwable cause() { getter for cause }
  char[] toString() {...}
  void print() {...} // should not allocate any memory
 }

 class Error : Throwable {...} // non-recoverable error
 class Exception : Throwable {...} // recoverable error

 The only Error subclasses should be AssertError, SwitchError. All
subclasses
 of Exception should have names that end with Exception.


 "Matthew" <admin.hat stlsoft.dot.org> wrote in message
 news:chrebn$12f7$1 digitaldaemon.com...
 I've persuaded big-W that the exception hierarchy refactoring can wait
no
 longer, but to do so I've had to volunteer to
 marshal the efforts.

 My plan is as follows:

 1. Solicit *on-topic* criticisms from the newsgroup

 2. Put on my code reviewer's hat, and do a detailed analysis and
criticism
 of the current situation. I'll do this
 *before* I read any of these criticisms, so as to be unbiased in the
review. This means you've a week or so before I'll
 be digesting and collating any responses from the community, so please
take your time to provide considered comments.
 3. Amalgamate these two sets of information (along with any prior posts
on
 the subject that I can easily find in the ng)
 into a (hopefully) simple recommendation for necessary changes to
Walter.
 (If it doesn't fit into three pages (+
 listings) or less, I shall consider it a failure.)

 4. Release the findings, and Walter's response (if it counters any of
the
 recommendations), to the community for
 *on-topic* debate.

 5. Implement any changes, gratefully taking advantage of any volunteers
who popped up their hands in steps 1. or 4..
 Note: please resist the temptation to wander OT, or get in slagging
matches. I'm barely less busy than Walter at the
 moment, so am just going to ignore any posts (and their sub-threads)
like
 that, and any useful criticisms contained
 therein will be wasted.

 Hopefully, by the time we hit 0.103 we should be in good shape. If the
process works, we might start a trend.
 Cheers

 Matthew
Sep 10 2004
next sibling parent Nick <Nick_member pathlink.com> writes:
How about a static opCall in exceptions? This can be done in two ways, I think:

1:







or 2: (my favourite)







Just my two cents...

Nick
Sep 10 2004
prev sibling next sibling parent reply Regan Heath <regan netwin.co.nz> writes:
I think we need a good definition of what is an 'Error' and what is an 
'Exception'.

I would have said 'OutOfMemory' was an error... however as per Matthew's 
request lets not debate this here :)

Ben, when you said "The only Error subclasses should be AssertError, 
SwitchError" did you mean?
   "The only Error subclasses (defined in std.exception) should be 
AssertError, SwitchError"
or
   "The only Error subclasses (that should ever exist) should be 
AssertError, SwitchError"
?

On Fri, 10 Sep 2004 10:40:50 -0400, Ben Hinkle <bhinkle mathworks.com> 
wrote:
 Let me add I'd like to see some standard exceptions, too. For example 
 there
 should be a module called something like std.exceptions that at least

 ArgumentException
 OutOfMemoryException
 NotSupportedException
 NotImplementedException
 IndexOutOfBoundsException

 I suggest this because I have several libraries that use these exceptions
 (except OutOfMemory, actually) and writing my own for each library is
 annoying and error-prone. For example if I want to perform some argument
 checking on an input to a function and throw an Exception if the 
 argument is
 illegal then I could write

 import std.exceptions;
 ...
 void foo(int must_be_positive) {
   if (must_be_positive <= 0)
     throw new ArgumentException("must_be_positive must be positive");
   ...
 }

 Plus having a standard "index out of bounds" exception comes in handy 
 with
 user-defined container classes.

 -Ben

 "Ben Hinkle" <bhinkle mathworks.com> wrote in message
 news:chsbiq$1hdf$1 digitaldaemon.com...

 Java
 names:

 class Throwable {
  this(char[] msg = null, Throwable cause = null) {...}
  Throwable cause() { getter for cause }
  char[] toString() {...}
  void print() {...} // should not allocate any memory
 }

 class Error : Throwable {...} // non-recoverable error
 class Exception : Throwable {...} // recoverable error

 The only Error subclasses should be AssertError, SwitchError. All
subclasses
 of Exception should have names that end with Exception.


 "Matthew" <admin.hat stlsoft.dot.org> wrote in message
 news:chrebn$12f7$1 digitaldaemon.com...
 I've persuaded big-W that the exception hierarchy refactoring can wait
no
 longer, but to do so I've had to volunteer to
 marshal the efforts.

 My plan is as follows:

 1. Solicit *on-topic* criticisms from the newsgroup

 2. Put on my code reviewer's hat, and do a detailed analysis and
criticism
 of the current situation. I'll do this
 *before* I read any of these criticisms, so as to be unbiased in the
review. This means you've a week or so before I'll
 be digesting and collating any responses from the community, so please
take your time to provide considered comments.
 3. Amalgamate these two sets of information (along with any prior 
posts
on
 the subject that I can easily find in the ng)
 into a (hopefully) simple recommendation for necessary changes to
Walter.
 (If it doesn't fit into three pages (+
 listings) or less, I shall consider it a failure.)

 4. Release the findings, and Walter's response (if it counters any of
the
 recommendations), to the community for
 *on-topic* debate.

 5. Implement any changes, gratefully taking advantage of any 
volunteers who popped up their hands in steps 1. or 4..
 Note: please resist the temptation to wander OT, or get in slagging
matches. I'm barely less busy than Walter at the
 moment, so am just going to ignore any posts (and their sub-threads)
like
 that, and any useful criticisms contained
 therein will be wasted.

 Hopefully, by the time we hit 0.103 we should be in good shape. If the
process works, we might start a trend.
 Cheers

 Matthew
-- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 11 2004
parent reply Ben Hinkle <bhinkle4 juno.com> writes:
 I would have said 'OutOfMemory' was an error... however as per Matthew's 
 request lets not debate this here :)
yeah - that could be. I just thought it should be an Exception because user code should be given a chance to free up cached objects that are sitting around in object pools and such. To abort the program because someone tried to allocate a massive object seems extreme.
 Ben, when you said "The only Error subclasses should be AssertError,
 SwitchError" did you mean?
    "The only Error subclasses (defined in std.exception) should be
 AssertError, SwitchError"
 or
    "The only Error subclasses (that should ever exist) should be
 AssertError, SwitchError"
 ?
I meant the first one. Users can define Errors - though I can't think of any off the top of my head.
Sep 12 2004
next sibling parent reply Regan Heath <regan netwin.co.nz> writes:
On Sun, 12 Sep 2004 11:57:09 -0400, Ben Hinkle <bhinkle4 juno.com> wrote:
 I would have said 'OutOfMemory' was an error... however as per Matthew's
 request lets not debate this here :)
yeah - that could be. I just thought it should be an Exception because user code should be given a chance to free up cached objects that are sitting around in object pools and such. To abort the program because someone tried to allocate a massive object seems extreme.
I agree. So are you also suggesting that an Error is uncatchable? This needs to be decided upon and well defined. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 12 2004
parent reply Ben Hinkle <bhinkle4 juno.com> writes:
Regan Heath wrote:

 On Sun, 12 Sep 2004 11:57:09 -0400, Ben Hinkle <bhinkle4 juno.com> wrote:
 I would have said 'OutOfMemory' was an error... however as per Matthew's
 request lets not debate this here :)
yeah - that could be. I just thought it should be an Exception because user code should be given a chance to free up cached objects that are sitting around in object pools and such. To abort the program because someone tried to allocate a massive object seems extreme.
I agree. So are you also suggesting that an Error is uncatchable?
Anything is catchable according to the language spec. The code try { ... } catch (Object e) { ... } will catch anything. The question is what should be caught when people are lazy and say try { ... } catch (Exception e) { ... } Should OutOfMemory be caught? Maybe, maybe not. What I would really like is the ability to register a delegate with the GC so that when it can't allocate any more memory it calls the delegate(s) to make a last-ditch effort to free up some memory and try the allocation again and if that fails then throw the exception. If the GC did that then I'd say go ahead and make OutOfMemory an Error because at that point there really isn't any memory left.
 This needs to be decided upon and well defined.
 
 Regan
 
Sep 12 2004
next sibling parent reply Regan Heath <regan netwin.co.nz> writes:
On Sun, 12 Sep 2004 21:50:52 -0400, Ben Hinkle <bhinkle4 juno.com> wrote:
 Regan Heath wrote:

 On Sun, 12 Sep 2004 11:57:09 -0400, Ben Hinkle <bhinkle4 juno.com> 
 wrote:
 I would have said 'OutOfMemory' was an error... however as per 
 Matthew's
 request lets not debate this here :)
yeah - that could be. I just thought it should be an Exception because user code should be given a chance to free up cached objects that are sitting around in object pools and such. To abort the program because someone tried to allocate a massive object seems extreme.
I agree. So are you also suggesting that an Error is uncatchable?
Anything is catchable according to the language spec. The code try { ... } catch (Object e) { ... } will catch anything.
Indeed. IIRC Matthew once suggested that Error should be uncatchable, I was wondering if anyone else felt that way too. My reservation against it is: What if a library is throwing an error (which should have perhaps been an exception), if you cannot catch it, and your application must not simply abort, you cannot use that library.
 The question is what should be caught when people are
 lazy and say
 try {
  ...
 } catch (Exception e) {
  ...
 }
 Should OutOfMemory be caught? Maybe, maybe not.
I think it depends on the application in question.
 What I would really like is the ability to register a delegate with the 
 GC so that when it can't
 allocate any more memory it calls the delegate(s) to make a last-ditch
 effort to free up some memory and try the allocation again and if that
 fails then throw the exception. If the GC did that then I'd say go ahead
 and make OutOfMemory an Error because at that point there really isn't 
 any memory left.
Yes, that would be nice. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 12 2004
parent reply Sean Kelly <sean f4.ca> writes:
Regan Heath wrote:

 On Sun, 12 Sep 2004 21:50:52 -0400, Ben Hinkle <bhinkle4 juno.com> wrote:

 Anything is catchable according to the language spec. The code
 try {
  ...
 } catch (Object e) {
  ...
 }
 will catch anything.
Indeed. IIRC Matthew once suggested that Error should be uncatchable, I was wondering if anyone else felt that way too.
I think Matthew said Errors should be unrecoverable, not uncatchable. Either way, that's how I would like to classify Errors. There's no point in throwing something that's uncatchable when you could just terminate the application instead.
 My reservation against it is: What if a library is throwing an error 
 (which should have perhaps been an exception), if you cannot catch it, 
 and your application must not simply abort, you cannot use that library.
If an Error is thrown, then the application is likely in an undefined state and can not continue anyway. Certainly not producing correct behavior anyway. I would prefer to have the application halt and have a process monitor attempt to restart the application.
 The question is what should be caught when people are
 lazy and say
 try {
  ...
 } catch (Exception e) {
  ...
 }
 Should OutOfMemory be caught? Maybe, maybe not.
I think it depends on the application in question.
 What I would really like is the ability to register a delegate with 
 the GC so that when it can't
 allocate any more memory it calls the delegate(s) to make a last-ditch
 effort to free up some memory and try the allocation again and if that
 fails then throw the exception. If the GC did that then I'd say go ahead
 and make OutOfMemory an Error because at that point there really isn't 
 any memory left.
Yes, that would be nice.
Agreed. Though it may still be nice to keep OutOfMemory as an exception. For example, say I'm crazy and write an application like this: Eventually the application is either going to run out of stack or heap space and will either explode or signal an OutOfMemory condition. Assuming OutOfMemory is signalled, a delegate may not be able to free up memory for another buffer allocation without some aggressively evil destruction of memory that should be left alone. However if an exception is thrown then the entire heap should be freed up purely as a side-effect of stack unwinding. In this case the application could continue to run just fine, and an Error would be overkill. Sean
Sep 12 2004
parent Regan Heath <regan netwin.co.nz> writes:
On Sun, 12 Sep 2004 22:28:35 -0700, Sean Kelly <sean f4.ca> wrote:
 Regan Heath wrote:

 On Sun, 12 Sep 2004 21:50:52 -0400, Ben Hinkle <bhinkle4 juno.com> 
 wrote:

 Anything is catchable according to the language spec. The code
 try {
  ...
 } catch (Object e) {
  ...
 }
 will catch anything.
Indeed. IIRC Matthew once suggested that Error should be uncatchable, I was wondering if anyone else felt that way too.
I think Matthew said Errors should be unrecoverable, not uncatchable.
Ahh yes, sorry for miss-representing you Matthew. :)
 Either way, that's how I would like to classify Errors.  There's no 
 point in throwing something that's uncatchable when you could just 
 terminate the application instead.
I agree. If you cannot catch it, there is no point in throwing it.
 My reservation against it is: What if a library is throwing an error 
 (which should have perhaps been an exception), if you cannot catch it, 
 and your application must not simply abort, you cannot use that library.
If an Error is thrown, then the application is likely in an undefined state and can not continue anyway. Certainly not producing correct behavior anyway. I would prefer to have the application halt and have a process monitor attempt to restart the application.
Agreed, provided the Error really does mean it's un-recoverable. My example was trying to suggest a miss classified error, or rather one that is not un-recoverable for this particular application. So the defintion of 'Error' is 'something that is un-recoverable' or something similar, I think we need a solid definition of this.
 The question is what should be caught when people are
 lazy and say
 try {
  ...
 } catch (Exception e) {
  ...
 }
 Should OutOfMemory be caught? Maybe, maybe not.
I think it depends on the application in question.
 What I would really like is the ability to register a delegate with 
 the GC so that when it can't
 allocate any more memory it calls the delegate(s) to make a last-ditch
 effort to free up some memory and try the allocation again and if that
 fails then throw the exception. If the GC did that then I'd say go 
 ahead
 and make OutOfMemory an Error because at that point there really isn't 
 any memory left.
Yes, that would be nice.
Agreed. Though it may still be nice to keep OutOfMemory as an exception. For example, say I'm crazy and write an application like this: Eventually the application is either going to run out of stack or heap space and will either explode or signal an OutOfMemory condition. Assuming OutOfMemory is signalled, a delegate may not be able to free up memory for another buffer allocation without some aggressively evil destruction of memory that should be left alone. However if an exception is thrown then the entire heap should be freed up purely as a side-effect of stack unwinding. In this case the application could continue to run just fine, and an Error would be overkill.
True, you've convinced me. I don't think OutOfMemory is un-recoverable. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 13 2004
prev sibling parent reply Nick <Nick_member pathlink.com> writes:
In article <ci2udp$gqr$1 digitaldaemon.com>, Ben Hinkle says...
The question is what should be caught when people are lazy and say
try {
 ...
} catch (Exception e) {
 ...
}

Should OutOfMemory be caught? Maybe, maybe not.
I don't think the above is lazy. In fact, I think that's the way it should be done (print out the error message and continue running.) An out of memory is usually not recoverable, so it should be an Error I think. But what is and is not recoverable can vary from program to program. If you want to continue on OutOfMemory, just catch it explicitly.
 What I would really like is
the ability to register a delegate with the GC so that when it can't
allocate any more memory it calls the delegate(s) to make a last-ditch
effort to free up some memory and try the allocation again and if that
fails then throw the exception. If the GC did that then I'd say go ahead
and make OutOfMemory an Error because at that point there really isn't any
memory left.
GC callback functions is a good idea, IMHO. A callback for before/after garbage collection runs, and maybe a "this object/memory area is about to be destructed/freed" callback. Nick
Sep 13 2004
parent reply Ben Hinkle <bhinkle4 juno.com> writes:
Nick wrote:

 In article <ci2udp$gqr$1 digitaldaemon.com>, Ben Hinkle says...
The question is what should be caught when people are lazy and say
try {
 ...
} catch (Exception e) {
 ...
}

Should OutOfMemory be caught? Maybe, maybe not.
I don't think the above is lazy. In fact, I think that's the way it should be done (print out the error message and continue running.) An out of memory is usually not recoverable, so it should be an Error I think. But what is and is not recoverable can vary from program to program. If you want to continue on OutOfMemory, just catch it explicitly.
Catching all exceptions is what I meant by lazy. Usually one should catch the smallest collection of exceptions as possible. Eg: catch StreamException or ThreadException etc. Catching all exceptions is a very wide net.
 What I would really like is
the ability to register a delegate with the GC so that when it can't
allocate any more memory it calls the delegate(s) to make a last-ditch
effort to free up some memory and try the allocation again and if that
fails then throw the exception. If the GC did that then I'd say go ahead
and make OutOfMemory an Error because at that point there really isn't any
memory left.
GC callback functions is a good idea, IMHO. A callback for before/after garbage collection runs, and maybe a "this object/memory area is about to be destructed/freed" callback.
I'm curious, what use-case do you have in mind for a per-object callback? I could imagine a post-destructor callback so that AJ (for instance) could install a memory-wiper routine.
 Nick
Sep 13 2004
parent Nick <Nick_member pathlink.com> writes:
In article <ci5a1k$1948$1 digitaldaemon.com>, Ben Hinkle says...
Nick wrote:
 I don't think the above is lazy. In fact, I think that's the way it should
 be done (print out the error message and continue running.) An out of
 memory is usually not recoverable, so it should be an Error I think. But
 what is and is not recoverable can vary from program to program. If you
 want to continue on OutOfMemory, just catch it explicitly.
Catching all exceptions is what I meant by lazy. Usually one should catch the smallest collection of exceptions as possible. Eg: catch StreamException or ThreadException etc. Catching all exceptions is a very wide net.
For many cases I agree with you, but in some cases you will want to catch all exceptions at one single point, without necessarily knowing what kind of exceptions they are. For example if you are calling external library functions.
 GC callback functions is a good idea, IMHO. A callback for before/after
 garbage collection runs, and maybe a "this object/memory area is about to
 be destructed/freed" callback.
I'm curious, what use-case do you have in mind for a per-object callback? I could imagine a post-destructor callback so that AJ (for instance) could install a memory-wiper routine.
I had an example in mind, but it isn't a very good example. I thought it could be used instead of finalizers when you have objects which depend on each other. For example: So now both objects are guaranteed to be alive when BufferedFile.flush() is called. But since you are not supposed to rely on object destruction, you are not guaranteed that callback() gets called at all. Therefore it is not a very usable example. Nick
Sep 14 2004
prev sibling parent Sean Kelly <sean f4.ca> writes:
Ben Hinkle wrote:
Ben, when you said "The only Error subclasses should be AssertError,
SwitchError" did you mean?
   "The only Error subclasses (defined in std.exception) should be
AssertError, SwitchError"
or
   "The only Error subclasses (that should ever exist) should be
AssertError, SwitchError"
?
I meant the first one. Users can define Errors - though I can't think of any off the top of my head.
They will probably mostly be platform-dependent. Something that results in memory corruption would be an Error, possibly some sort of halt instruction, etc. Sean
Sep 12 2004
prev sibling parent reply "Garett Bass" <gtbass studiotekne.com> writes:
"Ben Hinkle" wrote:
 I suggest this because I have several libraries that
 use these exceptions (except OutOfMemory, actually)
 and writing my own for each library is annoying and
 error-prone.
I don't understand why you would have to write your "own for each library". Why can't you simply write a single exception library that exposes these exceptions and have each of your other libraries use this exception library? Unless I misunderstand, this is exactly the purpose you are proposing that the phobos library's exception implementation would serve. Regards, Garett
Sep 12 2004
parent Ben Hinkle <bhinkle4 juno.com> writes:
Garett Bass wrote:

 "Ben Hinkle" wrote:
 I suggest this because I have several libraries that
 use these exceptions (except OutOfMemory, actually)
 and writing my own for each library is annoying and
 error-prone.
I don't understand why you would have to write your "own for each library". Why can't you simply write a single exception library that exposes these exceptions and have each of your other libraries use this exception library? Unless I misunderstand, this is exactly the purpose you are proposing that the phobos library's exception implementation would serve. Regards, Garett
true - I can share my own exceptions between the libraries that I write though it means the exceptions are not in the same package as the library and I have to make sure all my libraries have the same version of the exceptions. I like to keep my libraries independent so that installing one doesn't step on another. Mostly I was thinking of exceptions that make sense across libraries written by possibly different people.
Sep 12 2004
prev sibling next sibling parent Sean Kelly <sean f4.ca> writes:
Semi-related.  I just posted a bunch of exception issues to the bugs forum.
Link is here: http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D.bugs/1821
I'll write more about exceptions once I've had some time to think about the
issue.


Sean
Sep 10 2004
prev sibling next sibling parent reply Farmer <itsFarmer. freenet.de> writes:
"Matthew" <admin.hat stlsoft.dot.org> wrote in
news:chrebn$12f7$1 digitaldaemon.com: 

 I've persuaded big-W that the exception hierarchy refactoring can wait
 no longer, but to do so I've had to volunteer to marshal the efforts.
 
 My plan is as follows:
 
 1. Solicit *on-topic* criticisms from the newsgroup
 
 2. Put on my code reviewer's hat, and do a detailed analysis and
 criticism of the current situation. I'll do this *before* I read any of
 these criticisms, so as to be unbiased in the review. This means you've
 a week or so before I'll be digesting and collating any responses from
 the community, so please take your time to provide considered comments. 

[snip]
I propose this (over-engineered) java-like exception hierarchy: Object | |Throwable | |Error | | | |TooManyThreadsError | |OutOfMemoryError | |AssertionError | | | |ArrayBoundsError | |AssertError | |SwitchError | |Exception | |CheckedException | | | |FileException | ... |UncheckedException | |FormatException ... What's a Throwable? ------------------- By convention all exceptions derive from Throwable. It's still possible to throw any object, though. This way you can create your own exception hierarchy if needed. // Throwable contains an exception message and keeps track of // nested exceptions. class Throwable { private char[] msg; private Throwable next; this(char[] msg="", Throwable next=null); // returns a (user) message that describes the cause of the exception; // returns an empty string, if no message is available char[] getMessage(); // returns the nested exception or null if there is none Throwable getNext(); // returns a (somewhat cryptic) string char[] toString() { char[]rs=this.classinfo.name~": "~this.getMessage(); if (next) rs~="\n"~next.toString(); return rs; } } What are Errors? ---------------- Errors are thrown for exceptions that *might* require drastic means to recover from; like terminating the process or the thread, switching over to a backup system, rebooting computer (for Win9x), etc. When should an exception be derived from Error? 1.) when there are no guarantees when the exception occurs: At minimum a) there must be no resource leaks b) the destructor of an instance must be safely callable if an exception is thrown. If these minimum guarantees cannot be met, the exception is an Error. Furthermore, classes often have stronger guarantees, e.g. instance remains in a valid state if an exception is thrown. For cases that don't permit these stronger guarantees, an Error should be thrown instead of an Exception. 2.) when the exception might not be thrown in release builds: Although these exceptions could be safely catched in debug builds, the conditions that caused them would likely cause havoc for release builds. What about out of memory errors? ------------------------------- I argue that out of memory conditions are Errors, since in the days of virtual memory, most programmers assume that there is always enough memory available, unless a very large amount of memory is requested. Consequently, the vast majority of libraries don't come with exception guarantees when memory is running low. For the same reasons std.thread.Thread.start() should throw a TooManyThreadsError exception instead of a TooManyThreadsException. Why AssertionError? ------------------- Switch errors, array index errors and failed assert statements, mean all but one thing: A bug was revealed. // AssertionError doesn't provide an exception message, but stores // the location where the exception was thrown. class AssertionError : Error { private uint linnum; private char[] filename; this(char[] filename, uint linnum) { super(); this.filename=filename this.linnum=linnum } uint getLinenum(); char[] getFilename(); char[] toString() { return this.classinfo.name~" exception source: " ~filename~" ("~toString(linnum)~")"; } } Speaking of AssertionErrors, it would be useful if DMD offered an option "-spoiledprogrammer" for spoiled programmers. If this option is enabled, AssertionErrors would additionally contain * the function name for all AssertionErrors * the expression of an assert statement * the index that causes an ArrayBoundsError * the actual value that causes a SwitchError What are Exceptions? -------------------- Exceptions are thrown for conditions that leave the process in a defined state. Since Exceptions are thrown for predictable situations, it is usually easy to safely recover from the exceptional situation. We don't need no stinking checked exceptions! --------------------------------------------- Agreed, no checked exceptions. While the constant nagging of the Java compiler about undeclared checked exceptions isn't particularly useful, the concept behind them is. So, borrow the concept from Java, but not it's implementation. By convention, CheckedExceptions are thrown for errors that the programmer should carefully think about. Exceptions that derive from CheckedException * cannot be avoided by the programmer. For instance, opening a file, might fail: Even if you ensure that the file exists before opening the file, the file might have been deleted by the time it is actually opened! * might occur during the intended operation of a programm; they do not necessarily indicate bugs. * should be documented for all functions in the call hierarchy that might throw them When to use UncheckedExceptions ------------------------------- UncheckedExceptions are thrown for errors that don't require the programmer's attention, since these errors *usually* never happen. UncheckedExceptions usually indicate that a function is used in the wrong way by the caller. UncheckedExceptions * can be avoided for most use-cases. E.g. std.format.doFormat(...) throws a FormatException if the passed arguments do not match the format pattern. The exception can be avoided by passing a correct format pattern string. * usually indicate bugs, but not always * might not be documented in the function contract. Of course, it's good practice to do so, especially for functions that directly throw UncheckedExceptions. Errors vs. UncheckedExceptions ------------------------------ UncheckedExceptions * are part of the function's contract. As long as the contract isn't changed, the caller can rely on UncheckedExceptions beeing thrown. * leave the process in a defined state (exception guarantees like: no resource leaks, object instances remain valid, etc. permit that the the process can safely recover from the exception ) Errors * are not necessarily part of the function contract: Checks that throw Errors, might be absent in release builds or future versions of the function. * might leave the programm in an undefined state; should the Error be ignored, unpredictable follow-up errors might occur. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ I also like Ben's idea to include a bunch of generic exception classes in the standard library. This would be particularly handy for quick and dirty programming. Farmer. After writing this post, I recognized that the base class Throwable is a bit pointless for runtime generated Errors. Since these Errors neither have a user message nor a nested exception. So here's another, less java-like exception hierarchy. The presented guidelines for using Exceptions/Errors remain valid for this hierarchy. Object | |TooManyThreads |OutOfMemory |AssertionError | |ArrayBoundsError | |AssertError | |SwitchError | |Exception | |CheckedException // should be catched | | | |FileException | ... |UncheckedException // safe to catch | | | |FormatException | ... | |UnrecoverableError // unsafe to catch | |NullArgumentError ...
Sep 15 2004
next sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <Xns8FB0B405185D2itsFarmer 63.105.9.61>, Farmer says...
I propose this (over-engineered) java-like exception hierarchy:

Object
|
|Throwable
    |
    |Error
    |    |
    |    |TooManyThreadsError
    |    |OutOfMemoryError
    |    |AssertionError
    |        |
    |        |ArrayBoundsError
    |        |AssertError
    |        |SwitchError
    |
    |Exception
        |
        |CheckedException
        |    |
        |    |FileException
        |    ...
        |UncheckedException
             |
             |FormatException
             ...
..
What are Errors?
----------------

Errors are thrown for exceptions that *might* require drastic means to
recover from; like terminating the process or the thread, switching over
to a backup system, rebooting computer (for Win9x), etc. 

When should an exception be derived from Error?

1.) when there are no guarantees when the exception occurs:
 At minimum 
    a) there must be no resource leaks  
    b) the destructor of an instance must be safely callable 
if an exception is thrown.
If these minimum guarantees cannot be met, the exception is an Error.
In this case, I would argue that TooManyThreadsError and OutOfMemoryError should be derived from Exception, since they both meet these requirements.
Furthermore, classes often have stronger guarantees, e.g. instance remains
in a valid state if an exception is thrown. For cases that don't permit
these stronger guarantees, an Error should be thrown instead of an
Exception.
Could you go into this a bit more? Are you just saying that, basically, if a class invariant is violated then an Error should be thrown? If so, I *think* I agree but I'll have to think about it.
2.) when the exception might not be thrown in release builds:
Although these exceptions could be safely catched in debug builds, the
conditions that caused them would likely cause havoc for release builds. 
I'm having trouble thinking of an exception that might be recoverable in a debug build but not in a release build. Or do you mean only that they could be thrown and caught safely? And if this is the case, then I would argue that they shouldn't be Errors *or* Exceptions, but rather that the application should terminate with a core dump.
What about out of memory errors?
-------------------------------

I argue that out of memory conditions are Errors, since in the days of
virtual memory, most programmers assume that there is always enough memory
available, unless a very large amount of memory is requested.
For a specific class of applications, yes. But what about systems programming, embedded systems, etc? I might argue that such applications should be designed such that an out of memory condition should never occur, but they certainly aren't designed as if memory were unlimited. I think OutOfMemory should be an Exception because it's a situation that some applications may want to recover from, not because that is expected of all applications.
Consequently, the vast majority of libraries don't come with exception
guarantees when memory is running low.
I would argue that any library that is not exception safe is incorrect. It shouldn't matter what kind of exception it is. So I assume that any library that doesn't document otherwise at least provides the basic guarantee.
For the same reasons  std.thread.Thread.start()  should throw a
TooManyThreadsError exception instead of a TooManyThreadsException.
For the same reason, I think this should be an exception :) The user should be able to choose whether this constitutes an application failure. As for checked vs. unchecked exceptions, I'm not sure I understand the difference. Are you saying that checked exceptions should be handled explicitly while unchecked exceptions can be caught via the Exception base class, the error reported, and that's it? Obviously, all exceptions must be handled somewhere or the application will terminate. Sean
Sep 15 2004
parent reply Farmer <itsFarmer. freenet.de> writes:
First, to clear up one misunderstanding:
Unlike Matthew, I don't imply any mandatory way to recover from any kind of 
errors. It should be possible to freely choose how to deal with errors to the 
maximum extend that the given hardware permits.


Sean Kelly <sean f4.ca> wrote in news:cia1l4$1jfe$1 digitaldaemon.com:

 In article <Xns8FB0B405185D2itsFarmer 63.105.9.61>, Farmer says...
What are Errors?
----------------

Errors are thrown for exceptions that *might* require drastic means to
recover from; like terminating the process or the thread, switching over
to a backup system, rebooting computer (for Win9x), etc. 

When should an exception be derived from Error?

1.) when there are no guarantees when the exception occurs:
 At minimum 
    a) there must be no resource leaks  
    b) the destructor of an instance must be safely callable 
if an exception is thrown.
If these minimum guarantees cannot be met, the exception is an Error.
If I think more about it, destructors of instances must be callable at *any rate*. Otherwise use of auto classes (RAII) would become an unsafe practice.
 
 In this case, I would argue that TooManyThreadsError and
 OutOfMemoryError should be derived from Exception, since they both meet
 these requirements. 
 
Furthermore, classes often have stronger guarantees, e.g. instance
remains in a valid state if an exception is thrown. For cases that don't
permit these stronger guarantees, an Error should be thrown instead of
an Exception.
Could you go into this a bit more? Are you just saying that, basically, if a class invariant is violated then an Error should be thrown? If so, I *think* I agree but I'll have to think about it.
Yes. But this isn't restricted to class invariants. For example a function that adds an element to a container guarantees transactional behaviour in case of Exceptions. If for some reasons the function cannot ensure this guarantee, it could throw an Error as a last resort. E.g. A programmer puts an assert statement between statements that must never cause an exception. And now the assert statement actually fails.
2.) when the exception might not be thrown in release builds:
Although these exceptions could be safely catched in debug builds, the
conditions that caused them would likely cause havoc for release builds.
I'm having trouble thinking of an exception that might be recoverable in a debug build but not in a release build. Or do you mean only that they could be thrown and caught safely?
For example: void foo(Object o) { debug { if (o is null) throw new NullArgumentException(); } o.toString(); } main() { foo(null); } For debug builds an Exception is thrown that is definitely recoverable. But in release builds a seg fault is likely. What I meant is, that you should throw an NullArgumentError for such cases.
And if this is the case, then I
 would argue that they shouldn't be Errors *or* Exceptions, but rather
 that the application should terminate with a core dump.
Terminating the application might be the right thing for most cases, but it shouldn't be the only option.
What about out of memory errors?
-------------------------------

I argue that out of memory conditions are Errors, since in the days of
virtual memory, most programmers assume that there is always enough
memory available, unless a very large amount of memory is requested.
For a specific class of applications, yes. But what about systems programming, embedded systems, etc? I might argue that such applications should be designed such that an out of memory condition should never occur, but they certainly aren't designed as if memory were unlimited. I think OutOfMemory should be an Exception because it's a situation that some applications may want to recover from, not because that is expected of all applications.
It certainly depends on D's target audience: Is the target audience limited to pedantic programmers that do life critical applications? Or does it include programmers that rely on GCs to release resources? If you're doing systems programming and you designed your programm really correctly and use libraries that are equally well designed. Nothing shall stop you from catching an OutOfMemoryError and just continue.
Consequently, the vast majority of libraries don't come with exception
guarantees when memory is running low.
I would argue that any library that is not exception safe is incorrect. It shouldn't matter what kind of exception it is. So I assume that any library that doesn't document otherwise at least provides the basic guarantee.
I agree, but still argue that we programmers are suppossed to write *correct* programms (or at least working programms), based on *incorrect* libraries. Actually, the whole point of the exception hierarchy is, to make it easier to accomplish this.
For the same reasons  std.thread.Thread.start()  should throw a
TooManyThreadsError exception instead of a TooManyThreadsException.
For the same reason, I think this should be an exception :) The user should be able to choose whether this constitutes an application failure.
That's *one* option: provide a user setting that specifies what happens for a given kind of application failure.
 As for checked vs. unchecked exceptions, I'm not sure I understand the
 difference.  Are you saying that checked exceptions should be handled
 explicitly while unchecked exceptions can be caught via the Exception
 base class, the error reported, and that's it?  Obviously, all
 exceptions must be handled somewhere or the application will terminate.
 
No. While there might be a certain trend how checked vs. unchecked exceptions are typically handled. It is not the definite decision criteria. It's more important what the cause for an exception is: CheckedExceptions: cause is external: Caller cannot avoid them, consequently a programmer should usually focus on how to *handle* the exception. UncheckedExceptions: cause is internal: Caller can avoid them, consequently a programmer should usually focus on how to *prevent* the exception. Whether an exception is checked or unchecked depends on the anticipated use of an API. For example the function int atoi(char[] number) converts a string to an integer. If the passed string isn't a valid number should a CheckedException or an UncheckedException be thrown? It depends on the function's intended purpose: If its purpose is to convert a number then it should throw an UncheckedException. But if its purpose is to "check if a string is a number and convert it" then the function should throw a CheckedException. Farmer.
Sep 15 2004
parent reply Sean Kelly <sean f4.ca> writes:
In article <Xns8FB1A4F92693itsFarmer 63.105.9.61>, Farmer says...
First, to clear up one misunderstanding:
Unlike Matthew, I don't imply any mandatory way to recover from any kind of 
errors. It should be possible to freely choose how to deal with errors to the 
maximum extend that the given hardware permits.
Ah, thanks for clearing this up. That distinction between Exceptions and Errors is one I'd been assuming.
 Could you go into this a bit more?  Are you just saying that, basically,
 if a class invariant is violated then an Error should be thrown?  If so,
 I *think* I agree but I'll have to think about it.
Yes. But this isn't restricted to class invariants. For example a function that adds an element to a container guarantees transactional behaviour in case of Exceptions. If for some reasons the function cannot ensure this guarantee, it could throw an Error as a last resort. E.g. A programmer puts an assert statement between statements that must never cause an exception. And now the assert statement actually fails.
Ah okay. Then I do agree :)
 I'm having trouble thinking of an exception that might be recoverable in
 a debug build but not in a release build.  Or do you mean only that they
 could be thrown and caught safely?  
For example: void foo(Object o) { debug { if (o is null) throw new NullArgumentException(); } o.toString(); } main() { foo(null); } For debug builds an Exception is thrown that is definitely recoverable. But in release builds a seg fault is likely. What I meant is, that you should throw an NullArgumentError for such cases.
Gotcha. So it's an Error to make the programmer more likely to notice and fix the problem during development. In practice I would probably use asserts for this, which would classify them as errors anyway.
And if this is the case, then I
 would argue that they shouldn't be Errors *or* Exceptions, but rather
 that the application should terminate with a core dump.
Terminating the application might be the right thing for most cases, but it shouldn't be the only option.
True enough. I suppose even memory corruption is recoverable in some scenarios (segmented memory) so perhaps it's inadvisable to have the language mandate behavior in this regard. Though I suppose we're still stuck with termination if 2+ exceptions are thrown simultaneously.
For the same reasons  std.thread.Thread.start()  should throw a
TooManyThreadsError exception instead of a TooManyThreadsException.
For the same reason, I think this should be an exception :) The user should be able to choose whether this constitutes an application failure.
That's *one* option: provide a user setting that specifies what happens for a given kind of application failure.
This was due to my (incorrect) assumption that Errors necessitated program termination.
 As for checked vs. unchecked exceptions, I'm not sure I understand the
 difference.  Are you saying that checked exceptions should be handled
 explicitly while unchecked exceptions can be caught via the Exception
 base class, the error reported, and that's it?  Obviously, all
 exceptions must be handled somewhere or the application will terminate.
 
No. While there might be a certain trend how checked vs. unchecked exceptions are typically handled. It is not the definite decision criteria. It's more important what the cause for an exception is: CheckedExceptions: cause is external: Caller cannot avoid them, consequently a programmer should usually focus on how to *handle* the exception. UncheckedExceptions: cause is internal: Caller can avoid them, consequently a programmer should usually focus on how to *prevent* the exception.
Ah, that makes perfect sense. In that case, I think this is a valuable distinction to make. Good work! Sean
Sep 15 2004
parent reply Farmer <itsFarmer. freenet.de> writes:
Sean Kelly <sean f4.ca> wrote in news:ciajpr$1s3t$1 digitaldaemon.com:

 In article <Xns8FB1A4F92693itsFarmer 63.105.9.61>, Farmer says...
[snip]
 I'm having trouble thinking of an exception that might be recoverable
 in a debug build but not in a release build.  Or do you mean only that
 they could be thrown and caught safely?  
For example: void foo(Object o) { debug { if (o is null) throw new NullArgumentException(); } o.toString(); } main() { foo(null); } For debug builds an Exception is thrown that is definitely recoverable. But in release builds a seg fault is likely. What I meant is, that you should throw an NullArgumentError for such cases.
Gotcha. So it's an Error to make the programmer more likely to notice and fix the problem during development. In practice I would probably use asserts for this, which would classify them as errors anyway.
Yes, it's an Error, but making errors sometimes is a good thing. Asserts have one inconvenience: You can't tell at a glance what caused them. And who is responsible for the failure? The callee or the caller? That's why a programmer might prefer to throw an Error that contains additional information. But if an AssertError included: * the assertion expression * the location of the assertion: in-contract, out-contract, class- invariant or function body * stacktrace then in most cases, you could tell what causes an assertion, without using a debugger (or even looking at your code if you know it well).
And if this is the case, then I
 would argue that they shouldn't be Errors *or* Exceptions, but rather
 that the application should terminate with a core dump.
Terminating the application might be the right thing for most cases, but it shouldn't be the only option.
True enough. I suppose even memory corruption is recoverable in some scenarios (segmented memory) so perhaps it's inadvisable to have the language mandate behavior in this regard. Though I suppose we're still stuck with termination if 2+ exceptions are thrown simultaneously.
I'm not sure how D is supposed to handle this situation. I tried to simply throw an exception within a destructor (just 1 exception was active) but the programmed crashed with an exception error e0440001H. I shall take a look at the bug forum. I don't know if it's technically possible, but how about throwing a SimultanousExceptionError that contains both exceptions? Regards, Farmer.
Sep 16 2004
parent Sean Kelly <sean f4.ca> writes:
In article <Xns8FB1F10AFEB2AitsFarmer 63.105.9.61>, Farmer says...
Sean Kelly <sean f4.ca> wrote in news:ciajpr$1s3t$1 digitaldaemon.com:

 Gotcha.  So it's an Error to make the programmer more likely to notice
 and fix the problem during development.  In practice I would probably
 use asserts for this, which would classify them as errors anyway.
Yes, it's an Error, but making errors sometimes is a good thing. Asserts have one inconvenience: You can't tell at a glance what caused them. And who is responsible for the failure? The callee or the caller?
Combined with DBC, this should hopefully be pretty obvious, as I'd expect most asserts to be inside in/out blocks.
That's why a programmer might prefer to throw an Error that contains 
additional information. But if an AssertError included:
    * the assertion expression
    * the location of the assertion: in-contract, out-contract, class-
invariant or function body
    * stacktrace

then in most cases, you could tell what causes an assertion, without using a 
debugger (or even looking at your code if you know it well). 
Agreed. It would definately be nice if AssertError were more robust.
 True enough.  I suppose even memory corruption is recoverable in some
 scenarios (segmented memory) so perhaps it's inadvisable to have the
 language mandate behavior in this regard.  Though I suppose we're still
 stuck with termination if 2+ exceptions are thrown simultaneously.
I'm not sure how D is supposed to handle this situation. I tried to simply throw an exception within a destructor (just 1 exception was active) but the programmed crashed with an exception error e0440001H. I shall take a look at the bug forum.
I posted a bunch of bugs re: exceptions earlier this week. Basically, exceptions currently mess with stack unwinding such that auto classes aren't destructed when they should be. This makes testing the double-exception problem between difficult and impossible.
I don't know if it's technically possible, but how about throwing a 
SimultanousExceptionError that contains both exceptions? 
Good question. I think this is possible but perhaps it's technically difficult to implement. I'll have to do some digging and see if I can find the reasoning behind current C++ exception behavior (or perhaps Walter knows?). Sean
Sep 16 2004
prev sibling next sibling parent reply "Ben Hinkle" <bhinkle mathworks.com> writes:
 After writing this post, I recognized that the base class  Throwable  is a
 bit pointless for runtime generated Errors. Since these Errors neither
have a
 user message nor a nested exception.

 So here's another, less java-like exception hierarchy.
 The presented guidelines for using Exceptions/Errors remain valid for this
 hierarchy.


 Object
 |
 |TooManyThreads
 |OutOfMemory
 |AssertionError
 |   |ArrayBoundsError
 |   |AssertError
 |   |SwitchError
 |
 |Exception
     |
     |CheckedException    // should be catched
     |    |
     |    |FileException
     |    ...
     |UncheckedException  // safe to catch
     |    |
     |    |FormatException
     |    ...
     |
     |UnrecoverableError  // unsafe to catch
         |
         |NullArgumentError
         ...
Very nice post! You've convinced me of the general ideas. Some comments, though. It seems wierd to have Error at the top level and under UnrecoverableError. Also I'd like to toss around some more names (Checked and Unchecked don't mean too much to me). Finally I'd like ArrayBoundsError to subclass the more general (IllegalIndexException) and made into a MidSeverityException. Any class or container that wants to overload opIndex and is passed a bogus index can throw IllegalIndexException. Exception (has optional msg and cause. The message can contain the module and/or fcn ) | |LowSeverityException: sh*t happens | | | | FileException | | ArithmeticException | | |OverflowException | ... |MidSeverityException: most like a coding error to be tracked during debugging | | | | FormatException (maybe? I don't know what this is) | | IllegalIndexException: throw this when opIndex/Assign gets an illegal index | | ArrayBoundsException | | ArgumentException: throw this when someone passes you a bad input | | NotImplementedException: throw in a stub function that isn't done yet | | NotSupportedException: same as Java's - throw when something isn't supported. | ... |HighSeverityException: dangerous to continue program | | | | TooManyThreadsError | | OutOfMemoryError | | AssertError | ... To illustrate how to use the classification, let's take FormatException. Right now FormatException (actually it's currently called FormatError) is thrown in many contexts in std.format with a message that says what went wrong. So a bad input is FormatError("too few inputs") and int overflow is FormatError("int overflow"). I think rather than doing that format should throw ArgumentException("std.format: too few inputs") and OverflowException("std.format integer format overflow"). I'm a bit up in the air about putting the module or function in the message but as long as it is standardized it would be easy to trim that info out if one wanted to show the error message to a user. If we could get the stack trace out of an exception it would be much better, but getting the stack is non-trivial. -Ben
Sep 15 2004
parent reply Farmer <itsFarmer. freenet.de> writes:
"Ben Hinkle" <bhinkle mathworks.com> wrote in
news:cia7sd$1n6u$1 digitaldaemon.com: 

 After writing this post, I recognized that the base class  Throwable 
 is a bit pointless for runtime generated Errors. Since these Errors
 neither 
have a
 user message nor a nested exception.

 So here's another, less java-like exception hierarchy.
 The presented guidelines for using Exceptions/Errors remain valid for
 this hierarchy.


 Object
 |
 |TooManyThreads
 |OutOfMemory
 |AssertionError
 |   |ArrayBoundsError
 |   |AssertError
 |   |SwitchError
 |
 |Exception
     |
     |CheckedException    // should be catched
     |    |
     |    |FileException
     |    ...
     |UncheckedException  // safe to catch
     |    |
     |    |FormatException
     |    ...
     |
     |UnrecoverableError  // unsafe to catch
         |
         |NullArgumentError
         ...
Very nice post! You've convinced me of the general ideas. Some comments, though. It seems wierd to have Error at the top level and under UnrecoverableError.
I felt exactly the same way. And then I thought that the hierarchy is weird and asymmetrical: Could this be the D way? Here's the hierarchy again, with two changes (TooManyThreadsError was in the wrong place and everything ends with "Error" now): Object | |OutOfMemoryError |AssertionError | |ArrayBoundsError | |AssertError | |SwitchError | |Error |CheckedError // should be catched | |FileError | ... | |UncheckedError // safe to catch | |FormatError | ... | |UnrecoverableError // unsafe to catch |TooManyThreadsError ... I don't think, that it is too weird, as the only exceptions that are at the top level are truely special: They all belong to D's minimal RTL. These exceptions are directly mentioned in the D spec. For ArrayBoundsError, AssertError and SwitchError the constructor is even private, so only the compiler can create them. Also, this design has the advantage that it avoids any implication whether OutOfMemoryErrors and contract-violations are unrecoverable or not. Especially the opinions about OutOfMemoryError/Exception are quite opposite. except for OutOfMemoryError which had better been classified as an Exception) [From a purily philosophical point I even agree: OutOfMemoryError should be a checked exception!]
 Also I'd like to toss around some more names (Checked and Unchecked
 don't mean too much to me).
They don't mean anything to me, either. I thought about other names, but always came up with names that I feel are too specific and provoke wrong ad- hoc decisions on the part of the programmer. As an mnemonic aid I think of "Checked/Unchecked" as "I should check the exception" and "I should leave it unchecked".
 Finally I'd like ArrayBoundsError to
 subclass the more general (IllegalIndexException) and made into a
 MidSeverityException. Any class or container that wants to overload
 opIndex and is passed a bogus index can throw IllegalIndexException.
I disagree, since Errors vs. Exception cannot be mixed. If you wanted to throw an IllegalIndexError, then there's the problem that ArrayBoundsError already inherits from AssertionError. The other way round doesn't work too well either, since in general it is not possible to create an IllegalIndexException that contains a sensible filename linenumber context.
 
 Exception (has optional msg and cause. The message can contain the
 module and/or fcn )
 |
 |LowSeverityException: sh*t happens
 |   |
 |   | FileException
 |   | ArithmeticException
 |   |   |OverflowException
 |   ...
 |MidSeverityException: most like a coding error to be tracked during
 debugging
 |   |
 |   | FormatException (maybe? I don't know what this is)
 |   | IllegalIndexException: throw this when opIndex/Assign gets an
 |   | illegal 
 index
 |   |   ArrayBoundsException
 |   | ArgumentException: throw this when someone passes you a bad input
 |   | NotImplementedException: throw in a stub function that isn't done
 |   | yet NotSupportedException: same as Java's - throw when something
 |   | isn't 
 supported.
 |   ...
 |HighSeverityException: dangerous to continue program
 |   |
 |   | TooManyThreadsError
 |   | OutOfMemoryError
 |   | AssertError
 |   ...
 
 To illustrate how to use the classification, let's take FormatException.
 Right now FormatException (actually it's currently called FormatError)
 is thrown in many contexts in std.format with a message that says what
 went wrong. So a bad input is FormatError("too few inputs") and int
 overflow is FormatError("int overflow"). I think rather than doing that
 format should throw ArgumentException("std.format: too few inputs") and
 OverflowException("std.format integer format overflow").
I tend to agree about the ArgumentException, but I would prefer a MidSeverityException for all kinds of conversion errors, here. Initially I thought, that the more general exceptions, should be available as Low-, Mid- and High- Severity exceptions. The problem is that one would either have to make up 3 different names for one exception or have 3 classes that have the same name, but are in different modules. So, it seems this doesn't work so well with generic exceptions :-( So, It doesn't work! My conclusion is that the exception could be laid out in (at least) two ways: either * build up a deep class hierarchy in a "natural and object-oriented" way or * build up a rather shallow hierarchy around the notion of CheckedError, UncheckErrors and UnrecoverableErrors as virtually the only decision criteria Also we could simply forget about un/recoverable, un/checked and severities levels and just go with a simpler hierarchy like. Object | |OutOfMemoryError |AssertionError | |ArrayBoundsError | |AssertError | |SwitchError | |Error |PhobosError |... Probably, add a standard exception UnrecoverableError, if really needed.
 I'm a bit up in the air about putting the module or function in the
 message but as long as it is standardized it would be easy to trim that
 info out if one wanted to show the error message to a user. If 
That's a good idea. A standard way to include the code source of an exception, that is really needed: Why does doFormat() throw an FormatException instead of an ArgumentException, after all? I bet it is simply to know that the exception was caused by doFormat() or writef() and not by one of the zillons of other functions that all throw ArgumentException. But better don't put it into the exception message. Just store it in the separate property "source" and let Error.toString() include it. I'd like to have the exception message understandable for a technical-savy user, that knows nothing about programming. Things, that have only meaning to programmers should only be included by toString(). Farmer.
Sep 16 2004
next sibling parent Sean Kelly <sean f4.ca> writes:
Farmer wrote:
 
 Here's the hierarchy again, with two changes (TooManyThreadsError was in the 
 wrong place and everything ends with "Error" now):
 
  Object
  |
  |OutOfMemoryError
  |AssertionError
  |   |ArrayBoundsError
  |   |AssertError
  |   |SwitchError
  |
  |Error
      |CheckedError    // should be catched
      |   |FileError
      |   ...
      |
      |UncheckedError  // safe to catch
      |   |FormatError
      |   ...
      |
      |UnrecoverableError  // unsafe to catch
          |TooManyThreadsError
          ...
 
 I don't think, that it is too weird, as the only exceptions that are at the 
 top level are truely special: They all belong to D's minimal RTL. These 
 exceptions are directly mentioned in the D spec. For ArrayBoundsError, 
 AssertError and SwitchError the constructor is even private, so only the 
 compiler can create them.
 
 Also, this design has the advantage that it avoids any implication whether  
 OutOfMemoryErrors and contract-violations are unrecoverable or not.
I like having the system-generated exceptions at the top-level rather than derived from Error. Doing such allows them to be caught but it has to be done explicitly. This prevents unintentional recovery from an out of memory condition by catching Error. I also like the basic idea of checked and unchecked errors, but I'm still not sure I like building the error heirarchy around them.
 So, It doesn't work!
 
 My conclusion is that the exception could be laid out in (at least) two ways:
 either 
     * build up a deep class hierarchy in a "natural and object-oriented" way
 or
     * build up a rather shallow hierarchy around the notion of CheckedError, 
 UncheckErrors and UnrecoverableErrors as virtually the only decision criteria
 
 Also we could simply forget about un/recoverable, un/checked and severities 
 levels and just go with a simpler hierarchy like. 
 
 Object
  |
  |OutOfMemoryError
  |AssertionError
  |   |ArrayBoundsError
  |   |AssertError
  |   |SwitchError
  |
  |Error
      |PhobosError
           |...
 
 Probably, add a standard exception UnrecoverableError, if really needed.
I'm a bit more inclined to go this route, though I can't say why exactly. Maybe it's just my pragmatic nature assuming that programmers won't inherit from the proper exception with the other inheritance design. Sean
Sep 19 2004
prev sibling next sibling parent reply Farmer <itsFarmer. freenet.de> writes:
Farmer <itsFarmer. freenet.de> wrote in
news:Xns8FB1F10BEFD73itsFarmer 63.105.9.61: 

 "Ben Hinkle" <bhinkle mathworks.com> wrote in
 news:cia7sd$1n6u$1 digitaldaemon.com: 
 
 Also I'd like to toss around some more names (Checked and Unchecked
 don't mean too much to me).
They don't mean anything to me, either. I thought about other names, but always came up with names that I feel are too specific and provoke wrong ad- hoc decisions on the part of the programmer. As an mnemonic aid I think of "Checked/Unchecked" as "I should check the exception" and "I should leave it unchecked".
Just some alternative names for "checked/unchecked", that I made up myself or came across in articles, forums or wiki pages. unchecked exception: -------------------- ProgrammingError UnexpectedError UnpredictableError UnrecoverableError AvoidableError LogicalError checked exception: ------------------ GeneralError ExternalError ExpectedError PredictableError RecoverableError UnavoidableError EnvironmentError EnvironmentalError My favorites are ProgrammingError and EnvironmentError or GeneralError.
Sep 20 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Mon, 20 Sep 2004 15:13:17 +0000 (UTC), Farmer <itsFarmer. freenet.de> 
wrote:
 Farmer <itsFarmer. freenet.de> wrote in
 news:Xns8FB1F10BEFD73itsFarmer 63.105.9.61:

 "Ben Hinkle" <bhinkle mathworks.com> wrote in
 news:cia7sd$1n6u$1 digitaldaemon.com:

 Also I'd like to toss around some more names (Checked and Unchecked
 don't mean too much to me).
They don't mean anything to me, either. I thought about other names, but always came up with names that I feel are too specific and provoke wrong ad- hoc decisions on the part of the programmer. As an mnemonic aid I think of "Checked/Unchecked" as "I should check the exception" and "I should leave it unchecked".
Just some alternative names for "checked/unchecked", that I made up myself or came across in articles, forums or wiki pages. unchecked exception: -------------------- ProgrammingError UnexpectedError UnpredictableError UnrecoverableError AvoidableError LogicalError checked exception: ------------------ GeneralError ExternalError ExpectedError PredictableError RecoverableError UnavoidableError EnvironmentError EnvironmentalError My favorites are ProgrammingError and EnvironmentError or GeneralError.
Going by your previous descriptions..
 CheckedExceptions: cause is external: Caller cannot avoid them, 
 consequently a programmer should usually focus on how to *handle* the 
 exception.
this implies to me the problem is (perhaps) unique to this execution of the program, in other words it occurs only during runtime, as such I prefer 'RuntimeError'.
 UncheckedExceptions: cause is internal: Caller can avoid them, 
 consequently a programmer should usually focus on how to *prevent* the 
 exception.
this implies to me the problem is in the code, in other words every execution will have this problem, as such I prefer 'ProgramError' or 'DebugError'. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 20 2004
parent reply Farmer <itsFarmer. freenet.de> writes:
Regan Heath <regan netwin.co.nz> wrote in 
news:opsem9tqo15a2sq9 digitalmars.com:

[snip]
 
 
 Going by your previous descriptions..
 
 CheckedExceptions: cause is external: Caller cannot avoid them, 
 consequently a programmer should usually focus on how to *handle* the 
 exception.
this implies to me the problem is (perhaps) unique to this execution of the program, in other words it occurs only during runtime, as such I prefer 'RuntimeError'.
I wouldn't use the term 'RuntimeError' as it is too overloaded. Different people use it differently: RuntimeError = mainly programming errors [designers of Java] RuntimeError = errors of the JVM [critique of Java's exception hierarchy] RuntimeError = errors detected by the python interpreter [designers of the python] RuntimeError = anything that cannot be determined at compile time [you]
Sep 21 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Tue, 21 Sep 2004 08:34:14 +0000 (UTC), Farmer <itsFarmer. freenet.de> 
wrote:
 Regan Heath <regan netwin.co.nz> wrote in
 news:opsem9tqo15a2sq9 digitalmars.com:

 [snip]
 Going by your previous descriptions..

 CheckedExceptions: cause is external: Caller cannot avoid them,
 consequently a programmer should usually focus on how to *handle* the
 exception.
this implies to me the problem is (perhaps) unique to this execution of the program, in other words it occurs only during runtime, as such I prefer 'RuntimeError'.
I wouldn't use the term 'RuntimeError' as it is too overloaded. Different people use it differently: RuntimeError = mainly programming errors [designers of Java]
this is just plain wrong.
 RuntimeError = errors of the JVM [critique of Java's exception hierarchy]
which is why they changed it to this, which IMO is identical to
 RuntimeError = anything that cannot be determined at compile time [you]
which is my own understanding, and is also identical to
 RuntimeError = errors detected by the python interpreter [designers of 
 the python]
basically, all the above (except the flawed initial java one) are the same thing, errors that occur at the time of execution, or 'Runtime'. So I am even more sure now that 'RuntimeError' is the best term/name. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 21 2004
parent reply Nick <Nick_member pathlink.com> writes:
In article <opseo54ir25a2sq9 digitalmars.com>, Regan Heath says...
[snip]
basically, all the above (except the flawed initial java one) are the same 
thing, errors that occur at the time of execution, or 'Runtime'. So I am 
even more sure now that 'RuntimeError' is the best term/name.
I agree with your definition, but doesn't this mean that _all_ thrown exceptions/errors are runtime errors? Nick
Sep 22 2004
next sibling parent reply Sjoerd van Leent <svanleent wanadoo.nl> writes:
Nick wrote:
 In article <opseo54ir25a2sq9 digitalmars.com>, Regan Heath says...
 
[snip]
basically, all the above (except the flawed initial java one) are the same 
thing, errors that occur at the time of execution, or 'Runtime'. So I am 
even more sure now that 'RuntimeError' is the best term/name.
I agree with your definition, but doesn't this mean that _all_ thrown exceptions/errors are runtime errors? Nick
I disagree with this. A RuntimeError is an error which occurs when it is layered as deep as Phobos (The runtime environment) or when there is something very wrong with memory access and/or an error not catchable by the compiler, but still a programming fault. All other errors (thrown not as deep as previous are) are normal errors, for example, an error which is thrown when a Date value is incorrect, i.e.: February 30th, 2004. Regards, Sjoerd
Sep 22 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Wed, 22 Sep 2004 20:56:17 +0200, Sjoerd van Leent 
<svanleent wanadoo.nl> wrote:
 Nick wrote:
 In article <opseo54ir25a2sq9 digitalmars.com>, Regan Heath says...

 [snip]
 basically, all the above (except the flawed initial java one) are the 
 same thing, errors that occur at the time of execution, or 'Runtime'. 
 So I am even more sure now that 'RuntimeError' is the best term/name.
I agree with your definition, but doesn't this mean that _all_ thrown exceptions/errors are runtime errors? Nick
I disagree with this. A RuntimeError is an error which occurs when it is layered as deep as Phobos (The runtime environment) or when there is something very wrong with memory access and/or an error not catchable by the compiler, but still a programming fault.
That's not my definition. I based my definitions off Farmer's here: <quote> CheckedExceptions: cause is external: Caller cannot avoid them, consequently a programmer should usually focus on how to *handle* the exception. </unquote> in other words: - it may not occur all the time, on every execution - it may be a temporary failure What do you mean by "programming fault" exactly? Do you mean the code is simply wrong i.e. foo(NULL); throws InvalidParameter, meaning NULL is not acceptable? If so, that is not a runtime exception it's a program exception by my definition.
 All other errors (thrown not as deep as previous are) are normal errors, 
 for example, an error which is thrown when a Date value is incorrect, 
 i.e.: February 30th, 2004.
It depends where that date comes from, if it comes from a file then it's a runtime error, if it's hard coded then it's a program error. Do you see the distinction my definition tries to make? Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 22 2004
parent reply Nick <Nick_member pathlink.com> writes:
Seeing as different people seems to have different opinions on the definition of
a run time error, I think calling anything a RunTimeError will only cause
confusion. I suggest avoiding the term altogether.

Nick

In article <opseq5h3u45a2sq9 digitalmars.com>, Regan Heath says...
On Wed, 22 Sep 2004 20:56:17 +0200, Sjoerd van Leent 
<svanleent wanadoo.nl> wrote:
 Nick wrote:
 In article <opseo54ir25a2sq9 digitalmars.com>, Regan Heath says...

 [snip]
 basically, all the above (except the flawed initial java one) are the 
 same thing, errors that occur at the time of execution, or 'Runtime'. 
 So I am even more sure now that 'RuntimeError' is the best term/name.
I agree with your definition, but doesn't this mean that _all_ thrown exceptions/errors are runtime errors? Nick
I disagree with this. A RuntimeError is an error which occurs when it is layered as deep as Phobos (The runtime environment) or when there is something very wrong with memory access and/or an error not catchable by the compiler, but still a programming fault.
That's not my definition. I based my definitions off Farmer's here: <quote> CheckedExceptions: cause is external: Caller cannot avoid them, consequently a programmer should usually focus on how to *handle* the exception. </unquote> in other words: - it may not occur all the time, on every execution - it may be a temporary failure What do you mean by "programming fault" exactly? Do you mean the code is simply wrong i.e. foo(NULL); throws InvalidParameter, meaning NULL is not acceptable? If so, that is not a runtime exception it's a program exception by my definition.
 All other errors (thrown not as deep as previous are) are normal errors, 
 for example, an error which is thrown when a Date value is incorrect, 
 i.e.: February 30th, 2004.
It depends where that date comes from, if it comes from a file then it's a runtime error, if it's hard coded then it's a program error. Do you see the distinction my definition tries to make? Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 23 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Thu, 23 Sep 2004 10:24:28 +0000 (UTC), Nick <Nick_member pathlink.com> 
wrote:

 Seeing as different people seems to have different opinions on the 
 definition of
 a run time error, I think calling anything a RunTimeError will only cause
 confusion. I suggest avoiding the term altogether.
Well of course IMO other people have the 'wrong' definition of RunTime :) I think we can all agree runtime is the time at which it's running, can't we? Given that it's how you interpret the error that confuses things. I think this definition/explaination is what I mean. <quote me> I see what you mean, perhaps my definition is a little vague still, basically I looked at Farmers, here: <quote farmer> CheckedExceptions: cause is external: Caller cannot avoid them, consequently a programmer should usually focus on how to *handle* the exception. </unquote> and came to the conclusion that runtime errors were things that happened which were not a direct result of faulting coding, whereas program errors are a result of faulty coding. A good example of a program error is invalid parameter, this error occurs when you the programmer forgets to code a check to check a parameter, or maybe hard codes a parameter incorrectly. A good example of a runtime error is a ConnectionError, you get one of those if the thing you're connecting to is not accepting connections, but there is nothing wrong with the code itself, it will work if you connect to something that is listening. So you cannot prevent runtime errors but you can prevent/fix program errors. You want to handle/catch runtime errors, but not program errors. That is the distinction. </quote me> Regan
 Nick

 In article <opseq5h3u45a2sq9 digitalmars.com>, Regan Heath says...
 On Wed, 22 Sep 2004 20:56:17 +0200, Sjoerd van Leent
 <svanleent wanadoo.nl> wrote:
 Nick wrote:
 In article <opseo54ir25a2sq9 digitalmars.com>, Regan Heath says...

 [snip]
 basically, all the above (except the flawed initial java one) are the
 same thing, errors that occur at the time of execution, or 'Runtime'.
 So I am even more sure now that 'RuntimeError' is the best term/name.
I agree with your definition, but doesn't this mean that _all_ thrown exceptions/errors are runtime errors? Nick
I disagree with this. A RuntimeError is an error which occurs when it is layered as deep as Phobos (The runtime environment) or when there is something very wrong with memory access and/or an error not catchable by the compiler, but still a programming fault.
That's not my definition. I based my definitions off Farmer's here: <quote> CheckedExceptions: cause is external: Caller cannot avoid them, consequently a programmer should usually focus on how to *handle* the exception. </unquote> in other words: - it may not occur all the time, on every execution - it may be a temporary failure What do you mean by "programming fault" exactly? Do you mean the code is simply wrong i.e. foo(NULL); throws InvalidParameter, meaning NULL is not acceptable? If so, that is not a runtime exception it's a program exception by my definition.
 All other errors (thrown not as deep as previous are) are normal 
 errors,
 for example, an error which is thrown when a Date value is incorrect,
 i.e.: February 30th, 2004.
It depends where that date comes from, if it comes from a file then it's a runtime error, if it's hard coded then it's a program error. Do you see the distinction my definition tries to make? Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
-- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 23 2004
next sibling parent Nick <Nick_member pathlink.com> writes:
In article <opses4yynq5a2sq9 digitalmars.com>, Regan Heath says...
On Thu, 23 Sep 2004 10:24:28 +0000 (UTC), Nick <Nick_member pathlink.com> 
wrote:

 Seeing as different people seems to have different opinions on the 
 definition of
 a run time error, I think calling anything a RunTimeError will only cause
 confusion. I suggest avoiding the term altogether.
Well of course IMO other people have the 'wrong' definition of RunTime :) I think we can all agree runtime is the time at which it's running, can't we? Given that it's how you interpret the error that confuses things. I think this definition/explaination is what I mean. <quote me> I see what you mean, perhaps my definition is a little vague still, basically I looked at Farmers, here: <quote farmer> CheckedExceptions: cause is external: Caller cannot avoid them, consequently a programmer should usually focus on how to *handle* the exception. </unquote> and came to the conclusion that runtime errors were things that happened which were not a direct result of faulting coding, whereas program errors are a result of faulty coding. A good example of a program error is invalid parameter, this error occurs when you the programmer forgets to code a check to check a parameter, or maybe hard codes a parameter incorrectly. A good example of a runtime error is a ConnectionError, you get one of those if the thing you're connecting to is not accepting connections, but there is nothing wrong with the code itself, it will work if you connect to something that is listening. So you cannot prevent runtime errors but you can prevent/fix program errors. You want to handle/catch runtime errors, but not program errors. That is the distinction. </quote me> Regan
 Nick

 In article <opseq5h3u45a2sq9 digitalmars.com>, Regan Heath says...
 On Wed, 22 Sep 2004 20:56:17 +0200, Sjoerd van Leent
 <svanleent wanadoo.nl> wrote:
 Nick wrote:
 In article <opseo54ir25a2sq9 digitalmars.com>, Regan Heath says...

 [snip]
 basically, all the above (except the flawed initial java one) are the
 same thing, errors that occur at the time of execution, or 'Runtime'.
 So I am even more sure now that 'RuntimeError' is the best term/name.
I agree with your definition, but doesn't this mean that _all_ thrown exceptions/errors are runtime errors? Nick
I disagree with this. A RuntimeError is an error which occurs when it is layered as deep as Phobos (The runtime environment) or when there is something very wrong with memory access and/or an error not catchable by the compiler, but still a programming fault.
That's not my definition. I based my definitions off Farmer's here: <quote> CheckedExceptions: cause is external: Caller cannot avoid them, consequently a programmer should usually focus on how to *handle* the exception. </unquote> in other words: - it may not occur all the time, on every execution - it may be a temporary failure What do you mean by "programming fault" exactly? Do you mean the code is simply wrong i.e. foo(NULL); throws InvalidParameter, meaning NULL is not acceptable? If so, that is not a runtime exception it's a program exception by my definition.
 All other errors (thrown not as deep as previous are) are normal 
 errors,
 for example, an error which is thrown when a Date value is incorrect,
 i.e.: February 30th, 2004.
It depends where that date comes from, if it comes from a file then it's a runtime error, if it's hard coded then it's a program error. Do you see the distinction my definition tries to make? Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
-- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 30 2004
prev sibling parent Nick <Nick_member pathlink.com> writes:
Sorry for the late reply (and for the double post, stupid enter key...)

In article <opses4yynq5a2sq9 digitalmars.com>, Regan Heath says...
 Seeing as different people seems to have different opinions on the 
 definition of
 a run time error, I think calling anything a RunTimeError will only cause
 confusion. I suggest avoiding the term altogether.
Well of course IMO other people have the 'wrong' definition of RunTime :)
As I'm sure you know there's no such thing as a 'wrong' definition. There may however be inconsistencies between different definitions of the same term.
I think we can all agree runtime is the time at which it's running, can't 
we?
Yes.
Given that it's how you interpret the error that confuses things. I think 
this definition/explaination is what I mean.

<quote me>
I see what you mean, perhaps my definition is a little vague still, 
basically I looked at Farmers,
here:

<quote farmer>
CheckedExceptions: cause is external: Caller cannot avoid them, 
consequently a programmer should
usually focus on how to *handle* the exception.
</unquote>

and came to the conclusion that runtime errors were things that happened 
which were not a direct
result of faulting coding, whereas program errors are a result of faulty 
coding.
In short, you call something a runtime error if the _cause_ of the error happens at runtime, ie. it is external, whereas internal errors (bugs) you define not to be runtime errors. But, first of all, in the context of compiled languages, a runtime error is very often defined as any error that _occurs_ at runtime, in other words anything that is not a compile time error. So this definition might lead to confusion. Secondly I'm not sure if I agree that dividing exception classes into bugs/nonbug classes is very practical. For example in the date example, it's hard to tell (when throwing the exception) whether the invalid date came directly from the user ("runtime error") or a bad algorithm/hard coded date ("program error".) Nick
Sep 30 2004
prev sibling parent Regan Heath <regan netwin.co.nz> writes:
On Wed, 22 Sep 2004 14:10:36 +0000 (UTC), Nick <Nick_member pathlink.com> 
wrote:
 In article <opseo54ir25a2sq9 digitalmars.com>, Regan Heath says...
 [snip]
 basically, all the above (except the flawed initial java one) are the 
 same
 thing, errors that occur at the time of execution, or 'Runtime'. So I am
 even more sure now that 'RuntimeError' is the best term/name.
I agree with your definition, but doesn't this mean that _all_ thrown exceptions/errors are runtime errors?
I see what you mean, perhaps my definition is a little vague still, basically I looked at Farmers, here: <quote> CheckedExceptions: cause is external: Caller cannot avoid them, consequently a programmer should usually focus on how to *handle* the exception. </unquote> and came to the conclusion that runtime errors were things that happened which were not a direct result of faulting coding, whereas program errors are a result of faulty coding. A good example of a program error is invalid parameter, this error occurs when you the programmer forgets to code a check to check a parameter, or maybe hard codes a parameter incorrectly. A good example of a runtime error is a ConnectionError, you get one of those if the thing you're connecting to is not accepting connections, but there is nothing wrong with the code itself, it will work if you connect to something that is listening. So you cannot prevent runtime errors but you can prevent/fix program errors. You want to handle/catch runtime errors, but not program errors. That is the distinction. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 22 2004
prev sibling parent reply Farmer <itsFarmer. freenet.de> writes:
Farmer <itsFarmer. freenet.de> wrote in
news:Xns8FB1F10BEFD73itsFarmer 63.105.9.61: 
[snip]
 Initially I thought, that the more general exceptions, should be
 available as Low-, Mid- and High- Severity exceptions. The problem is
 that one would either have to make up 3 different names for one
 exception or have 3 classes that have the same name, but are in
 different modules. So, it seems this doesn't work so well with generic
 exceptions :-( 
 
 So, It doesn't work!
 
Example for the problem ----------------------- The obvious exception hierarchy for a database package could be: DBError QueryError DBConnectionError * QueryError is thrown if a query cannot be processed due to invalid syntax of an SQL statement * DBConnectionError is thrown if the connection to the DBMS cannot be established or breaks down. QueryError indicates a programming error (midseverity) while DBConnectionError indicates an environmental error (lowseverity). Unfortunately, it is impossible to include this information (programming vs. environmental error) in the hierarchy, since both exceptions already inherit from the general DBError class. Multi-Inheritance ----------------- One solution might be to use interfaces, as they allow multi-inheritance. DBError QueryError (implements IProgrammingError) DBConnectionError (implements IEnvironmentError) Currently, I'm not sure whether D supports catching interfaces: DMD compiled a simple example that catches an interface; at runtime the exception was even correctly catched, but the pointer-handle (reference) was bogus: when a method was called on the pointer an AV occured, although the pointer wasn't null. Even if D supports interfaces for exceptions. Every exception class has to explicitly dispatch all methods of an exception interface. A mixin should help here, though. Farmer.
Sep 20 2004
next sibling parent reply Regan Heath <regan netwin.co.nz> writes:
On Mon, 20 Sep 2004 15:13:18 +0000 (UTC), Farmer <itsFarmer. freenet.de> 
wrote:
 Farmer <itsFarmer. freenet.de> wrote in
 news:Xns8FB1F10BEFD73itsFarmer 63.105.9.61:
 [snip]
 Initially I thought, that the more general exceptions, should be
 available as Low-, Mid- and High- Severity exceptions. The problem is
 that one would either have to make up 3 different names for one
 exception or have 3 classes that have the same name, but are in
 different modules. So, it seems this doesn't work so well with generic
 exceptions :-(

 So, It doesn't work!
Example for the problem ----------------------- The obvious exception hierarchy for a database package could be: DBError QueryError DBConnectionError * QueryError is thrown if a query cannot be processed due to invalid syntax of an SQL statement * DBConnectionError is thrown if the connection to the DBMS cannot be established or breaks down. QueryError indicates a programming error (midseverity) while DBConnectionError indicates an environmental error (lowseverity). Unfortunately, it is impossible to include this information (programming vs. environmental error) in the hierarchy, since both exceptions already inherit from the general DBError class. Multi-Inheritance ----------------- One solution might be to use interfaces, as they allow multi-inheritance. DBError QueryError (implements IProgrammingError) DBConnectionError (implements IEnvironmentError) Currently, I'm not sure whether D supports catching interfaces: DMD compiled a simple example that catches an interface; at runtime the exception was even correctly catched, but the pointer-handle (reference) was bogus: when a method was called on the pointer an AV occured, although the pointer wasn't null. Even if D supports interfaces for exceptions. Every exception class has to explicitly dispatch all methods of an exception interface. A mixin should help here, though.
I think if you re-factor the tree into something like: (using my preferred names, yours are in ()) |RuntimeError(EnvironmentError) | |-DBError | |-DBConnectionError | |ProgramError(ProgrammingError) | |-SQLError | |-SQLSyntaxError then the problem vanishes. The justification for this refactoring is that SQLSyntaxError refers specifically to an SQL statement, which need not be associated with any particular database and/or connection to a database, i.e. you might write this function: bool verifySqlSyntax(char[] string){} and it might throw the SQLSyntaxError. Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 20 2004
parent reply Farmer <itsFarmer. freenet.de> writes:
Regan Heath <regan netwin.co.nz> wrote in 
news:opsem953pg5a2sq9 digitalmars.com:

 I think if you re-factor the tree into something like:
 (using my preferred names, yours are in ())
 
|RuntimeError(EnvironmentError)
| |-DBError
|    |-DBConnectionError
|
|ProgramError(ProgrammingError)
| |-SQLError
|    |-SQLSyntaxError
 
 then the problem vanishes.
The problem is, that some/most people want to have a common base class for all exceptions of a data base package.
 
 The justification for this refactoring is that SQLSyntaxError refers 
 specifically to an SQL statement, which need not be associated with any 
 particular database and/or connection to a database, i.e. you might write 
 this function:
Actually, it isn't always clear whether an SQLSyntaxError is a programming or an environmental error, since it depends on the database what syntax is supported.
 
 bool verifySqlSyntax(char[] string){}
 
 and it might throw the SQLSyntaxError.
 
 Regan
 
Sep 21 2004
parent Regan Heath <regan netwin.co.nz> writes:
On Tue, 21 Sep 2004 08:34:17 +0000 (UTC), Farmer <itsFarmer. freenet.de> 
wrote:

 Regan Heath <regan netwin.co.nz> wrote in
 news:opsem953pg5a2sq9 digitalmars.com:

 I think if you re-factor the tree into something like:
 (using my preferred names, yours are in ())

 |RuntimeError(EnvironmentError)
 | |-DBError
 |    |-DBConnectionError
 |
 |ProgramError(ProgrammingError)
 | |-SQLError
 |    |-SQLSyntaxError

 then the problem vanishes.
The problem is, that some/most people want to have a common base class for all exceptions of a data base package.
Why? I assume so they can go catch(DBError e) { } to catch any/all errors with the DB.
 The justification for this refactoring is that SQLSyntaxError refers
 specifically to an SQL statement, which need not be associated with any
 particular database and/or connection to a database, i.e. you might 
 write
 this function:
Actually, it isn't always clear whether an SQLSyntaxError is a programming or an environmental error, since it depends on the database what syntax is supported.
I didn't know that. So there are 2 types of syntax error, a program one and a runtime one. If you use program and runtime top level exceptions there is no way you can put all DB errors in the same tree, however, I don't think it matters, after all you don't want to catch the program errors, only the runtime ones so.. |RuntimeError(EnvironmentError) | |-DBError | |-DBConnectionError | |-SQLSyntaxError | |ProgramError(ProgrammingError) | |-ProgramDBError | |-ProgramSQLSyntaxError basically make the names of the program errors the same as the runtime ones, except with a prefix or something (after all you're not catching them in general so the runtime one should have the easy/intuitive name) Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Sep 21 2004
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
In article <Xns8FB5AEC64E2C6itsFarmer 63.105.9.61>, Farmer says...
Multi-Inheritance
-----------------

One solution might be to use interfaces, as they allow multi-inheritance.

DBError
   QueryError (implements IProgrammingError)
   DBConnectionError (implements IEnvironmentError)

Currently, I'm not sure whether D supports catching interfaces:
DMD compiled a simple example that catches an interface; at runtime the 
exception was even correctly catched, but the pointer-handle (reference) was 
bogus: when a method was called on the pointer an AV occured, although the 
pointer wasn't null.
If D doesn't support catching interfaces then it should. That the compiler doesn't balk at the syntax implies that this is legal and therefore a bug. Sean
Sep 21 2004
parent Farmer <itsFarmer. freenet.de> writes:
Sean Kelly <sean f4.ca> wrote in news:cipncc$1fqb$1 digitaldaemon.com:
 
 If D doesn't support catching interfaces then it should.  That the
 compiler doesn't balk at the syntax implies that this is legal and
 therefore a bug. 
 
I suppose, you're right. Unfortunately, interfaces are not so useful for the exception hierarchy. I can see only 2 possibilities. 1.) have separate branches for programming errors/environmental errors Error (implements IError) DBError QueryError (implements IProgrammingError) DBConnectionError (implements IEnvironmentError) advantage: By catching IEnvironmentError, it is easy to let IProgrammingErrors propagate. disadvantage: Every exception class must implement either IProgrammingError or IEnvironmentError. This requires some effort and therefore is unlikely to work well in practice. Furthermore it's possible to implement both IProgrammingError and IEnvironmentError by accident. 2.) only use interfaces to tag programming errors Error DBError QueryError (implements IProgrammingError) DBConnectionError advantage: Doesn't require much effort for library writers. disadvantage: To propagate IProgrammingErrors, more work is required: try { blah blah blah } catch (IProgrammingError e) { throw e; } catch (Error) { print ops, sth. is wrong with the data base. } That's almost the way it works in Java. Overall, I like the second approach better. It has less benefits, but is also far less obtrusive. Regards, Farmer.
Sep 22 2004
prev sibling parent "Matthew" <admin.hat stlsoft.dot.org> writes:
Just wanted to thank you for a very detailed and thought-provoking analysis.
I'll be examining this a lot more next week 
when I do my analysis, and may have some questions to level at you at that time.

Cheers

Matthew


"Farmer" <itsFarmer. freenet.de> wrote in message
news:Xns8FB0B405185D2itsFarmer 63.105.9.61...
 "Matthew" <admin.hat stlsoft.dot.org> wrote in
 news:chrebn$12f7$1 digitaldaemon.com:

 I've persuaded big-W that the exception hierarchy refactoring can wait
 no longer, but to do so I've had to volunteer to marshal the efforts.

 My plan is as follows:

 1. Solicit *on-topic* criticisms from the newsgroup

 2. Put on my code reviewer's hat, and do a detailed analysis and
 criticism of the current situation. I'll do this *before* I read any of
 these criticisms, so as to be unbiased in the review. This means you've
 a week or so before I'll be digesting and collating any responses from
 the community, so please take your time to provide considered comments.

[snip]
I propose this (over-engineered) java-like exception hierarchy: Object | |Throwable | |Error | | | |TooManyThreadsError | |OutOfMemoryError | |AssertionError | | | |ArrayBoundsError | |AssertError | |SwitchError | |Exception | |CheckedException | | | |FileException | ... |UncheckedException | |FormatException ... What's a Throwable? ------------------- By convention all exceptions derive from Throwable. It's still possible to throw any object, though. This way you can create your own exception hierarchy if needed. // Throwable contains an exception message and keeps track of // nested exceptions. class Throwable { private char[] msg; private Throwable next; this(char[] msg="", Throwable next=null); // returns a (user) message that describes the cause of the exception; // returns an empty string, if no message is available char[] getMessage(); // returns the nested exception or null if there is none Throwable getNext(); // returns a (somewhat cryptic) string char[] toString() { char[]rs=this.classinfo.name~": "~this.getMessage(); if (next) rs~="\n"~next.toString(); return rs; } } What are Errors? ---------------- Errors are thrown for exceptions that *might* require drastic means to recover from; like terminating the process or the thread, switching over to a backup system, rebooting computer (for Win9x), etc. When should an exception be derived from Error? 1.) when there are no guarantees when the exception occurs: At minimum a) there must be no resource leaks b) the destructor of an instance must be safely callable if an exception is thrown. If these minimum guarantees cannot be met, the exception is an Error. Furthermore, classes often have stronger guarantees, e.g. instance remains in a valid state if an exception is thrown. For cases that don't permit these stronger guarantees, an Error should be thrown instead of an Exception. 2.) when the exception might not be thrown in release builds: Although these exceptions could be safely catched in debug builds, the conditions that caused them would likely cause havoc for release builds. What about out of memory errors? ------------------------------- I argue that out of memory conditions are Errors, since in the days of virtual memory, most programmers assume that there is always enough memory available, unless a very large amount of memory is requested. Consequently, the vast majority of libraries don't come with exception guarantees when memory is running low. For the same reasons std.thread.Thread.start() should throw a TooManyThreadsError exception instead of a TooManyThreadsException. Why AssertionError? ------------------- Switch errors, array index errors and failed assert statements, mean all but one thing: A bug was revealed. // AssertionError doesn't provide an exception message, but stores // the location where the exception was thrown. class AssertionError : Error { private uint linnum; private char[] filename; this(char[] filename, uint linnum) { super(); this.filename=filename this.linnum=linnum } uint getLinenum(); char[] getFilename(); char[] toString() { return this.classinfo.name~" exception source: " ~filename~" ("~toString(linnum)~")"; } } Speaking of AssertionErrors, it would be useful if DMD offered an option "-spoiledprogrammer" for spoiled programmers. If this option is enabled, AssertionErrors would additionally contain * the function name for all AssertionErrors * the expression of an assert statement * the index that causes an ArrayBoundsError * the actual value that causes a SwitchError What are Exceptions? -------------------- Exceptions are thrown for conditions that leave the process in a defined state. Since Exceptions are thrown for predictable situations, it is usually easy to safely recover from the exceptional situation. We don't need no stinking checked exceptions! --------------------------------------------- Agreed, no checked exceptions. While the constant nagging of the Java compiler about undeclared checked exceptions isn't particularly useful, the concept behind them is. So, borrow the concept from Java, but not it's implementation. By convention, CheckedExceptions are thrown for errors that the programmer should carefully think about. Exceptions that derive from CheckedException * cannot be avoided by the programmer. For instance, opening a file, might fail: Even if you ensure that the file exists before opening the file, the file might have been deleted by the time it is actually opened! * might occur during the intended operation of a programm; they do not necessarily indicate bugs. * should be documented for all functions in the call hierarchy that might throw them When to use UncheckedExceptions ------------------------------- UncheckedExceptions are thrown for errors that don't require the programmer's attention, since these errors *usually* never happen. UncheckedExceptions usually indicate that a function is used in the wrong way by the caller. UncheckedExceptions * can be avoided for most use-cases. E.g. std.format.doFormat(...) throws a FormatException if the passed arguments do not match the format pattern. The exception can be avoided by passing a correct format pattern string. * usually indicate bugs, but not always * might not be documented in the function contract. Of course, it's good practice to do so, especially for functions that directly throw UncheckedExceptions. Errors vs. UncheckedExceptions ------------------------------ UncheckedExceptions * are part of the function's contract. As long as the contract isn't changed, the caller can rely on UncheckedExceptions beeing thrown. * leave the process in a defined state (exception guarantees like: no resource leaks, object instances remain valid, etc. permit that the the process can safely recover from the exception ) Errors * are not necessarily part of the function contract: Checks that throw Errors, might be absent in release builds or future versions of the function. * might leave the programm in an undefined state; should the Error be ignored, unpredictable follow-up errors might occur. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ I also like Ben's idea to include a bunch of generic exception classes in the standard library. This would be particularly handy for quick and dirty programming. Farmer. After writing this post, I recognized that the base class Throwable is a bit pointless for runtime generated Errors. Since these Errors neither have a user message nor a nested exception. So here's another, less java-like exception hierarchy. The presented guidelines for using Exceptions/Errors remain valid for this hierarchy. Object | |TooManyThreads |OutOfMemory |AssertionError | |ArrayBoundsError | |AssertError | |SwitchError | |Exception | |CheckedException // should be catched | | | |FileException | ... |UncheckedException // safe to catch | | | |FormatException | ... | |UnrecoverableError // unsafe to catch | |NullArgumentError ...
Sep 15 2004
prev sibling parent reply Farmer <itsFarmer. freenet.de> writes:
Here's an interesting proposal of an refactored exception hierarchy for 
Java(TM):

"Messy Exception Hierarchy" 
http://c2.com/cgi/wiki?MessyExceptionHierarchy



I found it when I googled a bit to widen my view about exceptions. More links 
follow:

*) "Standard Exception Classes in Python 1.5"
http://www.sourcekeg.co.uk/www.python.org/doc/essays/stdexceptions.html

<quote>
We looked into introducing more groups of related exceptions, but couldn't 
decide on the best grouping. In a language as dynamic as Python, it's hard to 
say whether TypeError is a "program error", a "runtime error" or an 
"environmental error", so we decided to leave it undecided. It could be 
argued that NameError and AttributeError should be derived from LookupError, 
but this is questionable and depends entirely on the application.
</quote>

Comment:
   * 3 classes of errors:  program errors, runtime errors and environmental 
errors
   * deciding about an exception hierarchy is very difficult: 
     both, grouping according to the 3 classes of errors and grouping by 
specialization was desired, but not wasn't manageable.



*) "Best Practices for Exception Handling"
http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html

Definitely, a good article about Exceptions for Java developers; though not 
everyone agrees with some of the recommended practices.


*) "Beware the dangers of generic Exceptions"
http://www.javaworld.com/javaworld/jw-10-2003/jw-1003-generics.html

An article that shows how dangerous (top-level?) generic Exceptions can be.



*) "Exceptional Java by Alan Griffiths"
http://www.octopull.demon.co.uk/java/ExceptionalJava.html

Report about difficulties that arised with exceptions in one real word 
project. The report also presents *one* solution for the encountered 
problems.



*) proto pattern "Generalize On Exception Behavior"
http://www.c2.com/cgi/wiki?GeneralizeOnExceptionBehavior

A good wiki page about the advantages and disadvantages of generalized 
exceptions. This page is informative and contains lots of sample code, but it 
also requires a lot of reading.


My insight about (Java) exceptions is, that in practise, Java programmers use 
exceptions very differently. This is somewhat surprising, since Java 
progammers usually share a very uniform way of dealing with problems. Indeed 
recommendations of actual Java programmers often contradict each other.
Sep 20 2004
parent Sean Kelly <sean f4.ca> writes:
In article <Xns8FB5AEC4C993AitsFarmer 63.105.9.61>, Farmer says...
*) "Best Practices for Exception Handling"
http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html
Thanks for the links. My Java experience is quite a few years out of date and this clarified some suspicions I had about checked vs. unchecked exceptions. While I like the idea of differentiating between programmer errors and other erros, I'm not sure how well this concept maps into D, because D (and C++) don't have any language support for checked exceptions. Further, I would argue (as I think I did in another part of this thread) that programmer errors should be handled by DBC and AssertErrors rather than inheriting from some alternate portion of the exception heirarchy. ie. I don't see much utility in having a programmer define separate generic catch blocks for checked and unchecked exceptions.
My insight about (Java) exceptions is, that in practise, Java programmers use 
exceptions very differently. This is somewhat surprising, since Java 
progammers usually share a very uniform way of dealing with problems. Indeed 
recommendations of actual Java programmers often contradict each other.
This has been my experience with Java as well, and is one reason why it never really appealed to me as a language. I think D would do better to somewhat follow C++ in this case and prefer a less structured and expansive exception heirarchy, at least so far as Phobos is concerned. All exceptions are unchecked by language design, but ideally still heirarchically arranged to allow for easy handling of different sorts of errors. Sean
Sep 30 2004