April 11, 2005
I'm looking into the Error/Exception situation in phobos and previous posts by Walter and others generally argued that Exceptions are recoverable and Errors are not. I believe there isn't an application-independent definition of what recoverable means and I would like to pursue an exception class hierarchy that doesn't distinguish the two (ie like C#). The distinction in Java is poorly designed and can be covered in D by subclassing Object directly. The existing class heirarchy in phobos and user code also seemingly randomly subclasses Error or Exception.

For example the class hierachy I have in mind looks like
Object
  OutOfMemory
  AssertionFailure
  Exception
    FileException
    StreamException
    ... etc, all the other exceptions and subclasses ...

where Exception is
class Exception {
    char[] msg;
    Object cause;
    this(char[] msg, Object cause = null);
    void print(); // print this exception and any causes
    char[] toString(); // string summarizes this exception
}

For reference see http://www.digitalmars.com/d/archives/digitalmars/D/6049.html http://www.digitalmars.com/d/archives/digitalmars/D/9556.html http://www.digitalmars.com/d/archives/digitalmars/D/10415.html

comments?



April 11, 2005
On Mon, 11 Apr 2005 19:37:21 -0400, Ben Hinkle <bhinkle@mathworks.com> wrote:
> I'm looking into the Error/Exception situation in phobos and previous posts
> by Walter and others generally argued that Exceptions are recoverable and
> Errors are not.

That seemed to me to be the general consesus.

> I believe there isn't an application-independent definition
> of what recoverable means and I would like to pursue an exception class
> hierarchy that doesn't distinguish the two (ie like C#).

I agree.

> The distinction in Java is poorly designed and can be covered in D by subclassing Object
> directly.

So if you want an un-recoverable error you subclass object and never catch Object directly?

> The existing class heirarchy in phobos and user code also
> seemingly randomly subclasses Error or Exception.

That's the way it seems to me also. That and the docs should note the exceptions/errors thrown by each method/function, but that's a task for after this one.

> For example the class hierachy I have in mind looks like
> Object
>   OutOfMemory
>   AssertionFailure
>   Exception
>     FileException
>     StreamException
>     ... etc, all the other exceptions and subclasses ...

Where does "missing/incorrect parameter" fit in? My feeling is that it's a subclass of Object and not Exception? or in fact should it be handled with an assert statement and thus be an AssertionFailure?

But you probably didn't want specifics at this point, so I'll be quiet now. ;)

> where Exception is
> class Exception {
>     char[] msg;
>     Object cause;
>     this(char[] msg, Object cause = null);
>     void print(); // print this exception and any causes
>     char[] toString(); // string summarizes this exception
> }
>
> For reference see
> http://www.digitalmars.com/d/archives/digitalmars/D/6049.html
> http://www.digitalmars.com/d/archives/digitalmars/D/9556.html
> http://www.digitalmars.com/d/archives/digitalmars/D/10415.html
>
> comments?

I think it's a great idea, I believe it needs to be done before D 1.0 and I'd like to help in any way I can.

Regan
April 12, 2005
>> The distinction in Java is poorly designed and can be covered in D by
>> subclassing Object
>> directly.
>
> So if you want an un-recoverable error you subclass object and never catch Object directly?

What do you have in mind as a user-defined unrecoverable error? I was
expecting users to subclass Exception (or some subclass of Exception) by
convention. The only case that comes to mind where I could see subclassing
something else would be to mimic the built-in AssertionFailure by
subclassing it and adding some custom behavior.
From a technical point of view anyone can throw or catch any Object.

>> For example the class hierachy I have in mind looks like
>> Object
>>   OutOfMemory
>>   AssertionFailure
>>   Exception
>>     FileException
>>     StreamException
>>     ... etc, all the other exceptions and subclasses ...
>
> Where does "missing/incorrect parameter" fit in? My feeling is that it's a subclass of Object and not Exception? or in fact should it be handled with an assert statement and thus be an AssertionFailure?

I see assertion failures as different than a incorrect parameters. An
assertion is a statement that must *always* be true no matter how the user
has called your function or used your object. If an assertion fails the
internal state of your part of the system is in doubt. By contrast an
incorrect parameter is to be expected. It can get confusing when one starts
to consider the user code as part of the same system as the function being
called, but then the user code can have asserts to make sure its own interal
state is consistent. To be concrete I was thinking that ArgumentException
would subclass Exception and have the .Net hierarchy
  Exception
    ArgumentException
      ArgumentNullException
      ArgumentOutOfRangeException

> But you probably didn't want specifics at this point, so I'll be quiet now. ;)

please ask any/all questions. The more the better.


April 12, 2005
On Mon, 11 Apr 2005 21:01:40 -0400, Ben Hinkle <ben.hinkle@gmail.com> wrote:
>>> The distinction in Java is poorly designed and can be covered in D by
>>> subclassing Object
>>> directly.
>>
>> So if you want an un-recoverable error you subclass object and never catch
>> Object directly?
>
> What do you have in mind as a user-defined unrecoverable error?

Nothing new, I was looking at:
  OutOfMemory
  AssertionFailure

and thought, for most applications these are treated as unrecoverable errors.

> I was
> expecting users to subclass Exception (or some subclass of Exception) by
> convention.

Agreed, 99% (maybe 100%) of the time. I wondered however whether it was possible for a user to want to create an unrecoverable error, i.e. for their application "Foo" is unrecoverable, if so they can do so, by subclassing Object, and ensuring they never catch(Object), or catch it in main and exit. Thus their application will always fail hard on "Foo".

> The only case that comes to mind where I could see subclassing
> something else would be to mimic the built-in AssertionFailure by
> subclassing it and adding some custom behavior.

Yeah, to add their own custom unrecoverable error of some sort.

> From a technical point of view anyone can throw or catch any Object.

Yep. I was thinking in terms of a style/convention which would be used to add custom unrecoverable errors.

>>> For example the class hierachy I have in mind looks like
>>> Object
>>>   OutOfMemory
>>>   AssertionFailure
>>>   Exception
>>>     FileException
>>>     StreamException
>>>     ... etc, all the other exceptions and subclasses ...
>>
>> Where does "missing/incorrect parameter" fit in? My feeling is that it's a
>> subclass of Object and not Exception? or in fact should it be handled with
>> an assert statement and thus be an AssertionFailure?
>
> I see assertion failures as different than a incorrect parameters. An
> assertion is a statement that must *always* be true no matter how the user
> has called your function or used your object. If an assertion fails the
> internal state of your part of the system is in doubt. By contrast an
> incorrect parameter is to be expected. It can get confusing when one starts
> to consider the user code as part of the same system as the function being
> called, but then the user code can have asserts to make sure its own interal
> state is consistent.

What you say makes sense to me. I've never really used assertions.
Where I was getting confused was that I was looking at it backwards, eg.

foo(5)
if '5' is an invalid parameter, this will *always* fail.

foo(<convert input from user to int>);
this will fail sometimes.

is there any difference in how the calls above should be handled? or should both simply be "ArgumentOutOfRangeException"

> To be concrete I was thinking that ArgumentException
> would subclass Exception and have the .Net hierarchy
>   Exception
>     ArgumentException
>       ArgumentNullException
>       ArgumentOutOfRangeException

Call me lazy but "ArgumentOutOfRangeException" seems like a really long name to use.
Apart from that, sounds good.

Regan
April 12, 2005
>> I was
>> expecting users to subclass Exception (or some subclass of Exception) by
>> convention.
>
> Agreed, 99% (maybe 100%) of the time. I wondered however whether it was possible for a user to want to create an unrecoverable error, i.e. for their application "Foo" is unrecoverable, if so they can do so, by subclassing Object, and ensuring they never catch(Object), or catch it in main and exit. Thus their application will always fail hard on "Foo".

ok. Seems fine to me.

>> The only case that comes to mind where I could see subclassing something else would be to mimic the built-in AssertionFailure by subclassing it and adding some custom behavior.
>
> Yeah, to add their own custom unrecoverable error of some sort.
>
>> From a technical point of view anyone can throw or catch any Object.
>
> Yep. I was thinking in terms of a style/convention which would be used to add custom unrecoverable errors.

I think if we have a convention something is wrong :-P. Why have a convention for something that is discouraged?

> foo(5)
> if '5' is an invalid parameter, this will *always* fail.
>
> foo(<convert input from user to int>);
> this will fail sometimes.
>
> is there any difference in how the calls above should be handled? or should both simply be "ArgumentOutOfRangeException"

Personally I would prefer that ArgumentOutOfRangeException be used for
arguments to functions that get checked at the start of the function. User
input should get treated more gracefully than throwing an exception IMO:
  class InputOutOfRangeException {...}
  bar() {
  barstart:
    int x = ...  prompt the user ...
    if (x == 5) {printf("try again doofus"); goto barstart;}
    foo(x);
  }
  void foo(int x) {
    if (x == 5) throw new ArgumentOutOfRangeException("Caller is a doofus");
  }

>> To be concrete I was thinking that ArgumentException
>> would subclass Exception and have the .Net hierarchy
>>   Exception
>>     ArgumentException
>>       ArgumentNullException
>>       ArgumentOutOfRangeException
>
> Call me lazy but "ArgumentOutOfRangeException" seems like a really long name to use.

Yeah. I agree. I'm tempted to suggest using Error instead of Exception
because it is easier to read and type but I have a feeling that would send
confusing signals to people used to "exceptions" and Exception. There was an
earlier thread about names but I only vaguely remember Exception "won".
Maybe we could shorten these ArgumentExceptions to ArgExceptions or
ParamException since it's obvious what Arg and Param mean. So how about
     ParamException
       ParamNullException
       ParamRangeException

> Apart from that, sounds good.

ok


April 12, 2005
On Mon, 11 Apr 2005 22:52:46 -0400, Ben Hinkle <ben.hinkle@gmail.com> wrote:
>>> The only case that comes to mind where I could see subclassing
>>> something else would be to mimic the built-in AssertionFailure by
>>> subclassing it and adding some custom behavior.
>>
>> Yeah, to add their own custom unrecoverable error of some sort.
>>
>>> From a technical point of view anyone can throw or catch any Object.
>>
>> Yep. I was thinking in terms of a style/convention which would be used to
>> add custom unrecoverable errors.
>
> I think if we have a convention something is wrong :-P. Why have a
> convention for something that is discouraged?

Because unless we address the issue (of defining a custom unrecoverable error) people will do it anyway and might do it in some other weird way. At least this way we can explain why it's discouraged, when it might be okay to use it and how to use it. I'm not suggesting we encourage it, just that we acknowledge it's existance and give a suggestion.

>> foo(5)
>> if '5' is an invalid parameter, this will *always* fail.
>>
>> foo(<convert input from user to int>);
>> this will fail sometimes.
>>
>> is there any difference in how the calls above should be handled? or
>> should both simply be "ArgumentOutOfRangeException"
>
> Personally I would prefer that ArgumentOutOfRangeException be used for
> arguments to functions that get checked at the start of the function. User
> input should get treated more gracefully than throwing an exception IMO:
>   class InputOutOfRangeException {...}
>   bar() {
>   barstart:
>     int x = ...  prompt the user ...
>     if (x == 5) {printf("try again doofus"); goto barstart;}
>     foo(x);
>   }
>   void foo(int x) {
>     if (x == 5) throw new ArgumentOutOfRangeException("Caller is a doofus");
>   }

Thanks, that makes sense.

>>> To be concrete I was thinking that ArgumentException
>>> would subclass Exception and have the .Net hierarchy
>>>   Exception
>>>     ArgumentException
>>>       ArgumentNullException
>>>       ArgumentOutOfRangeException
>>
>> Call me lazy but "ArgumentOutOfRangeException" seems like a really long
>> name to use.
>
> Yeah. I agree. I'm tempted to suggest using Error instead of Exception
> because it is easier to read and type but I have a feeling that would send
> confusing signals to people used to "exceptions" and Exception. There was an
> earlier thread about names but I only vaguely remember Exception "won".

I think you're right (on both points).

> Maybe we could shorten these ArgumentExceptions to ArgExceptions or
> ParamException since it's obvious what Arg and Param mean. So how about
>      ParamException
>        ParamNullException
>        ParamRangeException
>

Just thinking about this, null and out of range are effectively the same thing, as null is "out of range" for a parameter that "cannot be null'. So we *could* just combine them.

But then, of course, the reason for not combining them is so we can catch them seperately:

void foo(int a){}

try {
  foo(5);
} catch(ParamNullException e) {
} catch(ParamRangeException e) {
}

Otherwise we could just say:

class ParamException {
  this(char[] param, char[] problem) {
  }
}

And have it print "ParamException: (%s) is %s", eg:
"ParamException: (a) is out of range"
"ParamException: (a) is null"
"ParamException: (a) is not my favourite number"
"ParamException: (a) is unlucky for some"

<Warning: wacky idea>
It's a pity the class tree isn't more 'obvious' then we could simply drop the redundant 'Exception' alltogether. eg.

Exception
  Parameter
    Null
    OutOfRange

Is this:

try {
} catch(OutOfRange e) {
}

really that opaque that it requires this:

try {
} catch(ParamRangeException e) {
}

?

Of course, it then becomes possible to have collisions within the tree.

Exception
  Aaaaa
    Here
  Bbbbb
    Here

Exception.Aaaaa.Here
Exception.Bbbbb.Here

are both called "Here".
</wacky idea>

Regan
April 12, 2005
"Regan Heath" <regan@netwin.co.nz> wrote in message news:opso3kiga523k2f5@nrage.netwin.co.nz...
> On Mon, 11 Apr 2005 21:01:40 -0400, Ben Hinkle <ben.hinkle@gmail.com>  wrote:
>>>> The distinction in Java is poorly designed and can be covered
>>>> in D by
>>>> subclassing Object
>>>> directly.
>>>
>>> So if you want an un-recoverable error you subclass object and
>>> never  catch
>>> Object directly?
>>
>> What do you have in mind as a user-defined unrecoverable error?
>
> Nothing new, I was looking at:
>   OutOfMemory
>   AssertionFailure
>
> and thought, for most applications these are treated as unrecoverable  errors.

They may be, but that's quite wrong.

OutOfMemory is practically unrecoverable, but should not be classed
as an unrecoverable exception.
Conversely, AssertionFailure is practically recoverable, but most
certainly should be classed as unrecoverable. (And the language
should mandate and enforce the irrecoverability.)




April 12, 2005
"Ben Hinkle" <bhinkle@mathworks.com> wrote in message news:d3f1nh$rj4$1@digitaldaemon.com...
> I'm looking into the Error/Exception situation in phobos and
> previous posts
> by Walter and others generally argued that Exceptions are
> recoverable and
> Errors are not. I believe there isn't an application-independent
> definition
> of what recoverable means

An exception that may not be quenched, or in whose quenching the runtime takes over to terminate the process.

Naturally, the system should provide an opportunity for cleanup of arbitrary sophistication, but the infrastructure should not support resuming normal processing. However meandering the path - as a consequence of the requirements of the application (e.g. dumping unsaved data in as fine a form as possible) - it must lead to process termination.

Off the top of my head, only CP violations are unrecoverable, so it's not exactly

>and I would like to pursue an exception class
> hierarchy that doesn't distinguish the two (ie like C#). The
> distinction in
> Java is poorly designed and can be covered in D by subclassing
> Object
> directly. The existing class heirarchy in phobos and user code
> also
> seemingly randomly subclasses Error or Exception.

All of the that speaks to the likelihood that those hierarchies were ill-considered, rather than that there's an intrinsic problem in segregating the exceptions/errors. In my C++ work, I use exception classes (always derived directly/indirectly from std::exception) for exceptions, and types derived from ::stlsoft::unrecoverable for, er, unrecoverable errors. Works a treat, and never caused any probs. (I've had this stuff, coupled with a release-mode contract enforcement, and verbose, but immediate, application termination in a real-time multi-protocol financial system running the last 4-5 months. It caught two fundamental design oversights in the first week, and hasn't uttered a peep since. All is working tickety-boo.)

> For example the class hierachy I have in mind looks like
> Object
>  OutOfMemory
>  AssertionFailure
>  Exception
>    FileException
>    StreamException
>    ... etc, all the other exceptions and subclasses ...
>
> where Exception is
> class Exception {
>    char[] msg;
>    Object cause;
>    this(char[] msg, Object cause = null);
>    void print(); // print this exception and any causes
>    char[] toString(); // string summarizes this exception
> }
>
> For reference see http://www.digitalmars.com/d/archives/digitalmars/D/6049.html http://www.digitalmars.com/d/archives/digitalmars/D/9556.html http://www.digitalmars.com/d/archives/digitalmars/D/10415.html
>
> comments?

Problems with the above:
    1. I want to see the ability to throw/catch an Object removed.
(Or, if someone can possibly proffer a justification for wanting to
do that - what has it ever benefited anyone in C++ to be able to
throw a double? - incur a warning.) What's wrong with something
Throwable? It's not like anyone will (or at least should!) be mixing
an exception's duties with some other functionality, so there's no
need to fear the constraints of a strict single-inheritance
hierarchy.
    2. I grok that you've identified the strange nature of
OutOfMemory, in so far as it's theoretically recoverable but
practically irrecoverable (except when one is supplying custom
allocation). I agree that it's not a simple straightforward issue.
I'd suggest that this is the one area that needs more thinking
(including how other resource exhaustion might relate).
    3. AssertionFailure - which I presume is the parent to a more
detailed CP hierarchy - should be an Error, and irrecoverable (as
per discussion above).

I'd started work some months ago on suggestions for how Errors should be intrinsically handled within the language. I'll try and dig it out and post it. But, AFAICR, I posit that they should be analogous to function-try blocks in C++: it's fine (in fact professionally questionable not!) to catch them and exercise the application-specific level of informing of the user / dumping data / logging to SysLog/event-log/file/whatever, but the runtime, *in all threads*, will always rethrow any caught error, and will also provide intrinsic "catch(Error) { exit(EXIT_FAILURE); }" at the topmost level. The way I achieve this in the ::stlsoft::unrecoverable type is by ref-counting (similar to the shared_ptr mechanism) and the last one (copy, that is) out turns the lights off; it works a treat, as I mentioned above, and is not the least constricting, since it kindly waits until you've finished all your cleanup, even if you do several levels of cleanup by rethrowing the error. But since D holds exception objects by reference, all that jazz is unnecessary. In any case, such things are far better encapsulated in the language given that we (as I assert we do) want to avoid throwing of arbitrary types.

I know this is going to stir the same hornet's nest of people who feel like it's big-brother-ism, or who don't understand that a program that has violated its design is, from then on, invalid and open to *any* behaviour (obviously the more exotic things, like bringing down the internet or signalling to aliens that we want to be liberated by a benevolent megamilitarist, are somewhat less likely than the more prosaic). Killing it is the only right thing to do. And it's also the effective and convenient thing to do. As I've said, I've been running this stuff in the real world for some time, and clients always shit themselves at the prospect, but then completely come around when they see the speed at which the code reaches a point where it's no longer violating its design. In the words of the Pragmatic Programmers (section "DBC and Crashing Early", pp115, of TPP): "It's much easier to find and diagnose the problem by crashing early, at the site of the problem."

There is one valid scenario where errors should be quenchable: debuggers. For that, I have no immediate prescription, but I'm sure we can find a way to facilitate that without breaking the clean (and easily understand) delineation between exceptions and errors. (One idea might be that a base library function to turn on error quenching could be tied to the security infrastructure used by debuggers. Those with more experience than me in writing debuggers can no doubt some sense of this.)

Charon



April 12, 2005
> Off the top of my head, only CP violations are unrecoverable, so it's not exactly

onerous or limiting



April 12, 2005
On Tue, 12 Apr 2005 15:16:55 +1000, Matthew <admin@stlsoft.dot.dot.dot.dot.org> wrote:
> "Regan Heath" <regan@netwin.co.nz> wrote in message
> news:opso3kiga523k2f5@nrage.netwin.co.nz...
>> On Mon, 11 Apr 2005 21:01:40 -0400, Ben Hinkle
>> <ben.hinkle@gmail.com>  wrote:
>>>>> The distinction in Java is poorly designed and can be covered
>>>>> in D by
>>>>> subclassing Object
>>>>> directly.
>>>>
>>>> So if you want an un-recoverable error you subclass object and
>>>> never  catch
>>>> Object directly?
>>>
>>> What do you have in mind as a user-defined unrecoverable error?
>>
>> Nothing new, I was looking at:
>>   OutOfMemory
>>   AssertionFailure
>>
>> and thought, for most applications these are treated as
>> unrecoverable  errors.
>
> They may be, but that's quite wrong.
>
> OutOfMemory is practically unrecoverable, but should not be classed
> as an unrecoverable exception.

Agreed, in part, why "class" it as anything but what it is?

> Conversely, AssertionFailure is practically recoverable, but most
> certainly should be classed as unrecoverable.

As above, why "class" it as anything but what it is?

> (And the language
> should mandate and enforce the irrecoverability.)

Disagree.

Regan
« First   ‹ Prev
1 2 3 4 5 6 7 8 9 10 11
Top | Discussion index | About this forum | D home