View mode: basic / threaded / horizontal-split · Log in · Help
April 01, 2013
DIP33: A standard exception hierarchy
It's time to clean up this mess.

http://wiki.dlang.org/DIP33
April 01, 2013
Re: DIP33: A standard exception hierarchy
On Monday, 1 April 2013 at 11:08:16 UTC, Lars T. Kyllingstad 
wrote:
> It's time to clean up this mess.
>
> http://wiki.dlang.org/DIP33

A quick comment about your "Error" section. You say:

"In general, Errors should not be caught, primarily because they 
indicate that the program logic is compromised, and that the 
program may therefore be in an invalid state from which there is 
no recovery".

It is actually much worst than that: errors bypass the entire 
exception handling mechanism, blasting through code that would 
handle destructors, and even flying through functions that are 
nothrow. They don't just indicate a "potential" invalid state, 
they actually *put* the program in an invalid state, from which 
there is no recovery.

That is the main mechanical difference between an "error" and an 
"exception", it is not just a philosophical "logic vs runtime".

--------

Under this situation, I'm wondering how the "OutOfMemory" is 
dealt with (you don't explain). The only logical explanation I 
can see is:
- It is not an exception and is not caught by "catch(Exception)".
- But it is not an error either, so does not corrupt the program 
state.
=> Goal: It is hard to catch, but you *can* recover from it.
Is this correct? Is this what we are going for?

--------

Other than that, I think it would be beneficial to clean up our 
exception architecture.
April 01, 2013
Re: DIP33: A standard exception hierarchy
On Monday, 1 April 2013 at 11:23:50 UTC, monarch_dodra wrote:
> On Monday, 1 April 2013 at 11:08:16 UTC, Lars T. Kyllingstad 
> wrote:
>> It's time to clean up this mess.
>>
>> http://wiki.dlang.org/DIP33
>
> A quick comment about your "Error" section. You say:
>
> "In general, Errors should not be caught, primarily because 
> they indicate that the program logic is compromised, and that 
> the program may therefore be in an invalid state from which 
> there is no recovery".
>
> It is actually much worst than that: errors bypass the entire 
> exception handling mechanism, blasting through code that would 
> handle destructors, and even flying through functions that are 
> nothrow. They don't just indicate a "potential" invalid state, 
> they actually *put* the program in an invalid state, from which 
> there is no recovery.
>
> That is the main mechanical difference between an "error" and 
> an "exception", it is not just a philosophical "logic vs 
> runtime".

I had forgotten about that.  Personally, I think that is crazy.  
Errors ought to propagate just like Exceptions, they just 
shouldn't be a part of your "normal" error-handling mechanism.


> --------
>
> Under this situation, I'm wondering how the "OutOfMemory" is 
> dealt with (you don't explain). The only logical explanation I 
> can see is:
> - It is not an exception and is not caught by 
> "catch(Exception)".
> - But it is not an error either, so does not corrupt the 
> program state.
> => Goal: It is hard to catch, but you *can* recover from it.
> Is this correct? Is this what we are going for?

That was the idea, yes.
April 01, 2013
Re: DIP33: A standard exception hierarchy
On Monday, 1 April 2013 at 11:33:57 UTC, Lars T. Kyllingstad 
wrote:
> On Monday, 1 April 2013 at 11:23:50 UTC, monarch_dodra wrote:
>> On Monday, 1 April 2013 at 11:08:16 UTC, Lars T. Kyllingstad 
>> wrote:
>>> It's time to clean up this mess.
>>>
>>> http://wiki.dlang.org/DIP33
>>
>> A quick comment about your "Error" section. You say:
>>
>> "In general, Errors should not be caught, primarily because 
>> they indicate that the program logic is compromised, and that 
>> the program may therefore be in an invalid state from which 
>> there is no recovery".
>>
>> It is actually much worst than that: errors bypass the entire 
>> exception handling mechanism, blasting through code that would 
>> handle destructors, and even flying through functions that are 
>> nothrow. They don't just indicate a "potential" invalid state, 
>> they actually *put* the program in an invalid state, from 
>> which there is no recovery.
>>
>> That is the main mechanical difference between an "error" and 
>> an "exception", it is not just a philosophical "logic vs 
>> runtime".
>
> I had forgotten about that.  Personally, I think that is crazy.
>  Errors ought to propagate just like Exceptions, they just 
> shouldn't be a part of your "normal" error-handling mechanism.

I don't know, I find the approach kind of genius personally. If 
it is a logic error, and your program is going to die, then why 
pay for cleanup? You are getting the radical efficiency of a 
normal assert, but with the opportunity to die like with an 
exception.

The nicest part (IMO), is that thanks to this, you can assert in 
a nothrow function, without violating its nothrow-ness (which we 
do all over phobos, and even with built-in arrays).

>> --------
>>
>> Under this situation, I'm wondering how the "OutOfMemory" is 
>> dealt with (you don't explain). The only logical explanation I 
>> can see is:
>> - It is not an exception and is not caught by 
>> "catch(Exception)".
>> - But it is not an error either, so does not corrupt the 
>> program state.
>> => Goal: It is hard to catch, but you *can* recover from it.
>> Is this correct? Is this what we are going for?
>
> That was the idea, yes.

I like the idea, but it would be particularly breaking change for 
nothrow functions though:

void foo() nothrow
{
    try
    {
        throw new OutOfMemory()
    }
    catch (Exception /+e+/)
        {}
}

"Error: foo is nothrow yet may throw"

...what ...? But I caught the Exception!

The bypass would be giving OutOfMemory the same semantics as an 
Error, but then, it would just be an actual Error...
April 01, 2013
Re: DIP33: A standard exception hierarchy
On Monday, 1 April 2013 at 11:52:47 UTC, monarch_dodra wrote:
> On Monday, 1 April 2013 at 11:33:57 UTC, Lars T. Kyllingstad 
> wrote:
>> On Monday, 1 April 2013 at 11:23:50 UTC, monarch_dodra wrote:
>>> On Monday, 1 April 2013 at 11:08:16 UTC, Lars T. Kyllingstad 
>>> wrote:
>>>> It's time to clean up this mess.
>>>>
>>>> http://wiki.dlang.org/DIP33
>>>
>>> A quick comment about your "Error" section. You say:
>>>
>>> "In general, Errors should not be caught, primarily because 
>>> they indicate that the program logic is compromised, and that 
>>> the program may therefore be in an invalid state from which 
>>> there is no recovery".
>>>
>>> It is actually much worst than that: errors bypass the entire 
>>> exception handling mechanism, blasting through code that 
>>> would handle destructors, and even flying through functions 
>>> that are nothrow. They don't just indicate a "potential" 
>>> invalid state, they actually *put* the program in an invalid 
>>> state, from which there is no recovery.
>>>
>>> That is the main mechanical difference between an "error" and 
>>> an "exception", it is not just a philosophical "logic vs 
>>> runtime".
>>
>> I had forgotten about that.  Personally, I think that is crazy.
>> Errors ought to propagate just like Exceptions, they just 
>> shouldn't be a part of your "normal" error-handling mechanism.
>
> I don't know, I find the approach kind of genius personally. If 
> it is a logic error, and your program is going to die, then why 
> pay for cleanup? You are getting the radical efficiency of a 
> normal assert, but with the opportunity to die like with an 
> exception.

But if all cleanup code is bypassed, what is the point in using 
the exception mechanism in the first place?  Why not just abort() 
and be done with it?

I can think of two reasons for throwing an Error rather than 
aborting directly:
1. You want a kind of "graceful" shutdown, in which destructors 
*are* called and make their best attempt at cleaning things up.
2. You want to catch it at some point, and perform some manual 
cleanup.

But if (1) does not happen, can you even hope to do something 
useful with (2)?  Your program is in the worst possible state it 
can be!

> The nicest part (IMO), is that thanks to this, you can assert 
> in a nothrow function, without violating its nothrow-ness 
> (which we do all over phobos, and even with built-in arrays).

Well, there are two benefits to nothrow:
1. It makes a guarantee to the programmer that a function does 
not throw, and the programmer consequently does not need to worry 
about exception handling.
2. The compiler can elide exception handling code altogether, 
which improves performance.

Personally I think (1) is the most important, and we could 
maintain this guarantee even if Error and OutOfMemory propagated 
just like Exception.  We'd just redefine 'nothrow' to mean "does 
not throw Exception".

Then, we could introduce a new attribute, e.g. __hard_nothrow, to 
allow for (2).  This would require the programmer to handle Error 
and OutOfMemory too, and importantly, we could apply it to most C 
functions.


>>> Under this situation, I'm wondering how the "OutOfMemory" is 
>>> dealt with (you don't explain). The only logical explanation 
>>> I can see is:
>>> - It is not an exception and is not caught by 
>>> "catch(Exception)".
>>> - But it is not an error either, so does not corrupt the 
>>> program state.
>>> => Goal: It is hard to catch, but you *can* recover from it.
>>> Is this correct? Is this what we are going for?
>>
>> That was the idea, yes.
>
> I like the idea, but it would be particularly breaking change 
> for nothrow functions though:
>
> void foo() nothrow
> {
>     try
>     {
>         throw new OutOfMemory()
>     }
>     catch (Exception /+e+/)
>         {}
> }
>
> "Error: foo is nothrow yet may throw"
>
> ...what ...? But I caught the Exception!
>
> The bypass would be giving OutOfMemory the same semantics as an 
> Error, but then, it would just be an actual Error...

I think OutOfMemory should not be restricted by nothrow, and I 
propose to solve it as described above.

Lars
April 01, 2013
Re: DIP33: A standard exception hierarchy
01.04.2013 15:08, Lars T. Kyllingstad пишет:
> It's time to clean up this mess.
>
> http://wiki.dlang.org/DIP33

Personally I like current "Error = die" approach because of 
opportunities it gives for `nothrow` in release build.

About `FormatError`: I'd like make formatting an error but... There is 
no `isValidFormat` in Phobos yet.

-- 
Денис В. Шеломовский
Denis V. Shelomovskij
April 01, 2013
Re: DIP33: A standard exception hierarchy
On 2013-04-01 13:08, Lars T. Kyllingstad wrote:
> It's time to clean up this mess.
>
> http://wiki.dlang.org/DIP33

In general I think it looks good. I also think it's really needed.

Don't IOException and ProcessException need a "kind" field as well?

-- 
/Jacob Carlborg
April 01, 2013
Re: DIP33: A standard exception hierarchy
On Monday, 1 April 2013 at 12:12:56 UTC, Lars T. Kyllingstad 
wrote:

> But if all cleanup code is bypassed, what is the point in using 
> the exception mechanism in the first place?  Why not just 
> abort() and be done with it?
>
> I can think of two reasons for throwing an Error rather than 
> aborting directly:
> 1. You want a kind of "graceful" shutdown, in which destructors 
> *are* called and make their best attempt at cleaning things up.
> 2. You want to catch it at some point, and perform some manual 
> cleanup.
>
> But if (1) does not happen, can you even hope to do something 
> useful with (2)?  Your program is in the worst possible state 
> it can be!

I'm no expert on these things, but:

Any chance of being in an invalid state - > undefined behaviour

Undefined behaviour - > your destructors/cleanup routine could in 
theory do anything.

Therefore, you're better off not trying to cleanup if program 
state could be invalid.


Anything that doesn't signal a possible invalid state should be 
sensibly catchable and run destructors etc. , anything that does 
should cut through the program like a knife and is catchable at 
your own risk.
April 01, 2013
Re: DIP33: A standard exception hierarchy
On Mon, Apr 01, 2013 at 01:08:15PM +0200, Lars T. Kyllingstad wrote:
> It's time to clean up this mess.
> 
> http://wiki.dlang.org/DIP33

I'd prefer "NetworkException" instead of "NetworkingException" (long
name with no added advantage).

About the use of enums in FilesystemException, NetworkingException,
etc.: I understand the rationale for them, but this also makes them
closed for extension. Is there anything fundamentally wrong with
creating subclasses of these exceptions instead of attempting to cover
*all* possible problems in a single enum?

I like the use of chaining to attach errno or windows system errors to
exceptions. This solves the problem of errno's not being easily mapped
to one of the standard exception classes. It's sort of the reverse of
what chaining was intended for (another exception being thrown while the
first one was in transit), but I haven't actually seen any real use case
for the latter, so we might as well use it for the purpose here.

The only thing is, this makes library code a bit trickier to write.
Maybe Phobos needs to provide some kind of standard (though
system-specific) way of mapping errno, windows error codes, etc., into
one of the standard exception types, so that this mapping won't have to
be duplicated all over the place. Obviously it can't be completely
automatic, since some errno's may map to different exceptions depending
on context, but *some* amount of mapping would be highly desirable to
avoid code duplication.

Another nice thing to have (not sure how practical it will be) is to add
more information to the exceptions under Exception. To truly free us
from the tendency to just invent GetoptException, XMLException,
RegexException, etc., we need to consider that sometimes you *do* want
to know where the exception came from. For example, you could be calling
std.getopt from some generic initialization code that does other stuff
too, both of which may throw a ConversionException, say. Sometimes you
need to distinguish between them (display a command-line syntax help in
one case, just display an error in the other case, depending on where
the exception came from). One solution is to add a locus field to
Exception:

	class Exception : Error {
		...
		/// Where this exception came from
		/// E.g., "std.getopt", "std.xml",
		/// "my.program.init.complexconv", etc..
		string locus;
	}

This way the catching code doesn't have to downcast, guess, or do some
ugly non-portable hacking to figure out what to do.

This field should probably be automatically filled by Exception's ctor,
so that it doesn't require additional burden on the programmer.

I'm not 100% sure the module name should be used in this field, but the
idea is that it should contain some way of identifying the origin of the
exception that can be programmatically identified.


T

-- 
Two wrongs don't make a right; but three rights do make a left...
April 01, 2013
Re: DIP33: A standard exception hierarchy
On Monday, 1 April 2013 at 11:08:16 UTC, Lars T. Kyllingstad 
wrote:
> It's time to clean up this mess.
>
> http://wiki.dlang.org/DIP33

I like the idea, I think some specifics may need worked out 
(already being discussed)

You're likely purposely avoiding this. But when the subject of a 
"Exception Hierarchy" comes up thoughts of having specific 
modules to house the exceptions come up. I don't think this is 
the primary concern, but my position is to keep the exceptions in 
their std implementation/use.
« First   ‹ Prev
1 2 3 4 5
Top | Discussion index | About this forum | D home