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 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lars T. Kyllingstad | 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 | ||||
---|---|---|---|---|
| ||||
Posted in reply to monarch_dodra | 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 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lars T. Kyllingstad | 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 | ||||
---|---|---|---|---|
| ||||
Posted in reply to monarch_dodra | 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 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lars T. Kyllingstad | 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 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lars T. Kyllingstad | 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 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lars T. Kyllingstad | 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 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lars T. Kyllingstad | 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 | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lars T. Kyllingstad | 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.
|
Copyright © 1999-2021 by the D Language Foundation