www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Error derived from Exception is WRONG, DAMNIT

reply downs <default_357-line yahoo.de> writes:
Why is Error, in 2.0, _STILL_ derived from Exception?

I understand that this has come up before, which makes it all the more
confusing that it's _STILL_ not fixed. And make no mistake, this isn't just a
matter of semantics, but an actual bug causing actual problems, such as
unexpected crashes.

Let's see, the only use of making unrecoverable Errors a *special case* of
recoverable Exceptions would be, um, if you wanted to catch unrecoverable
Errors only ...

Does anybody else see a problem with that statement? Like, that you're not
supposed to catch unrecoverable errors in the first place?

The problem with this ordering is that you end up _accidentally_ catching
unrecoverable errors, which in turn leads to maybe not even noticing some
critical error in debug mode, which in turn leads to different behavior between
debug mode and release mode, such as mysterious, unexpected segmentation faults
(say on Array Bounds Violation). I don't have to explain why this is a bad
thing.

Could this be changed? _Please_?

 --downs, slightly agitated :)
Mar 01 2008
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
downs wrote:
 Why is Error, in 2.0, _STILL_ derived from Exception?
 
 I understand that this has come up before, which makes it all the more
confusing that it's _STILL_ not fixed. And make no mistake, this isn't just a
matter of semantics, but an actual bug causing actual problems, such as
unexpected crashes.
 
 Let's see, the only use of making unrecoverable Errors a *special case* of
recoverable Exceptions would be, um, if you wanted to catch unrecoverable
Errors only ...
 
 Does anybody else see a problem with that statement? Like, that you're not
supposed to catch unrecoverable errors in the first place?
 
 The problem with this ordering is that you end up _accidentally_ catching
unrecoverable errors, which in turn leads to maybe not even noticing some
critical error in debug mode, which in turn leads to different behavior between
debug mode and release mode, such as mysterious, unexpected segmentation faults
(say on Array Bounds Violation). I don't have to explain why this is a bad
thing.
 
 Could this be changed? _Please_?
 
  --downs, slightly agitated :)
+1-hoping-for-sudden-outbreak-of-common-sense --bb
Mar 01 2008
prev sibling next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
downs wrote:
 Why is Error, in 2.0, _STILL_ derived from Exception?
 
 I understand that this has come up before, which makes it all the more
confusing that it's _STILL_ not fixed. And make no mistake, this isn't just a
matter of semantics, but an actual bug causing actual problems, such as
unexpected crashes.
 
 Let's see, the only use of making unrecoverable Errors a *special case* of
recoverable Exceptions would be, um, if you wanted to catch unrecoverable
Errors only ...
 
 Does anybody else see a problem with that statement? Like, that you're not
supposed to catch unrecoverable errors in the first place?
 
 The problem with this ordering is that you end up _accidentally_ catching
unrecoverable errors, which in turn leads to maybe not even noticing some
critical error in debug mode, which in turn leads to different behavior between
debug mode and release mode, such as mysterious, unexpected segmentation faults
(say on Array Bounds Violation). I don't have to explain why this is a bad
thing.
 
 Could this be changed? _Please_?
 
  --downs, slightly agitated :)
Agreed. While we're at it, let's switch around Java's Exception and RuntimeException, since RuntimeExceptions are also generally not supposed to be caught.
Mar 01 2008
prev sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
== Quote from downs (default_357-line yahoo.de)'s article
 Why is Error, in 2.0, _STILL_ derived from Exception?
 I understand that this has come up before, which makes it all the more
confusing that it's _STILL_ not
fixed. And make no mistake, this isn't just a matter of semantics, but an actual bug causing actual problems, such as unexpected crashes.
 Let's see, the only use of making unrecoverable Errors a *special case* of
recoverable Exceptions
would be, um, if you wanted to catch unrecoverable Errors only ...
 Does anybody else see a problem with that statement? Like, that you're not
supposed to catch
unrecoverable errors in the first place?
 The problem with this ordering is that you end up _accidentally_ catching
unrecoverable errors, which
in turn leads to maybe not even noticing some critical error in debug mode, which in turn leads to different behavior between debug mode and release mode, such as mysterious, unexpected segmentation faults (say on Array Bounds Violation). I don't have to explain why this is a bad thing.
 Could this be changed? _Please_?
  --downs, slightly agitated :)
I'm not sure I agree. First, I think it's important to note that all Errors are Exceptions, so the inheritance relationship makes sense. Also, I feel that all throwable objects should be classified as Exceptions, so I don't like the idea of having them both be siblings derived from Throwable. Finally, while it may be easy to catch and ignore Exceptions, I feel very strongly that this is bad programming practice. Exceptions should never be caught and ignored--particularly at such a granularity. I would prefer that a library stick to its guns and inspire a change in programmer mindset than to make allowances for such mistakes. Sean
Mar 01 2008
next sibling parent downs <default_357-line yahoo.de> writes:
Sean Kelly wrote:
 == Quote from downs (default_357-line yahoo.de)'s article
 Why is Error, in 2.0, _STILL_ derived from Exception?
 I understand that this has come up before, which makes it all the more
confusing that it's _STILL_ not
fixed. And make no mistake, this isn't just a matter of semantics, but an actual bug causing actual problems, such as unexpected crashes.
 Let's see, the only use of making unrecoverable Errors a *special case* of
recoverable Exceptions
would be, um, if you wanted to catch unrecoverable Errors only ...
 Does anybody else see a problem with that statement? Like, that you're not
supposed to catch
unrecoverable errors in the first place?
 The problem with this ordering is that you end up _accidentally_ catching
unrecoverable errors, which
in turn leads to maybe not even noticing some critical error in debug mode, which in turn leads to different behavior between debug mode and release mode, such as mysterious, unexpected segmentation faults (say on Array Bounds Violation). I don't have to explain why this is a bad thing.
 Could this be changed? _Please_?
  --downs, slightly agitated :)
I'm not sure I agree. First, I think it's important to note that all Errors are Exceptions, so the inheritance relationship makes sense.
All Errors are Exceptions because we say so. This model has no inherent value except from the point of view of the current Phobos implementation. It makes more sense, from a user POV, to say that Exceptions are special cases of Errors that can be caught.
 Also, I feel that all throwable objects should be classified as Exceptions, so
I
 don't like the idea of having them both be siblings derived from Throwable.
Neither did I suggest this; actually, my thought was Exception : Error :)
 Finally, while it may be
 easy to catch and ignore Exceptions, I feel very strongly that this is bad
programming practice.
 Exceptions should never be caught and ignored--particularly at such a
granularity.
 I would prefer that a library stick to its guns and inspire a change in
programmer mindset than to make allowances for such mistakes.
What about cases where we just want to see if some operation succeeds, and if not, try something else instead, _regardless_ of the reason why it failed? Also, I'd argue that such a change would not be for the better. The whole *point* of Exceptions is that they are recoverable - if I want to catch and discard all of them, I don't want the compiler to give me moral lessons about error handling, I want it to *get the :bleep: out of my way and let me do what I want to do.* Not crash and burn in release mode. :)
 
 
 Sean
--downs
Mar 01 2008
prev sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Sean Kelly wrote:
 I'm not sure I agree.  First, I think it's important to note that all Errors
are Exceptions, so the inheritance
 relationship makes sense.  Also, I feel that all throwable objects should be
classified as Exceptions, so I
 don't like the idea of having them both be siblings derived from Throwable. 
Finally, while it may be
 easy to catch and ignore Exceptions, I feel very strongly that this is bad
programming practice.
 Exceptions should never be caught and ignored--particularly at such a
granularity.  I would prefer that
 a library stick to its guns and inspire a change in programmer mindset than to
make allowances for such
 mistakes.


 Sean
   
The classical OOP example of a square that inherits from a rectangle comes to mind. sure, you can say that a square is a rectangle but inheritance in this case will cause breakage. for instance you can double one of the rectangle's edges. squares that inherit that method will break because if you double only one edge than you no longer have a square. My point is that errors have the same relation to exceptions. There is additional functionality that exceptions provide ( you can catch them and do stuff) which are not part of an error's interface. Errors should represent unrecoverable situations where the program must terminate. catching an error is, as downs said, a BUG. -- Yigal
Mar 01 2008
next sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
== Quote from Yigal Chripun (yigal100 gmail.com)'s article
 My point is that errors have the same relation to exceptions. There is
 additional functionality that  exceptions provide ( you can catch them
 and do stuff) which are not part of an error's interface. Errors should
 represent unrecoverable situations where the program must terminate.
 catching an error is, as downs said, a BUG.
So the problem then becomes which errors to classify as non-recoverable. Particularly in a systems language like D, I would be hesitant to dictate that almost any errors must necessarily terminate the program. Consider an array bounds error. This is a system-generated error that clearly indicates a program flaw, and yet the program is still in a valid state. The same could be said of other errors as well. When considering adding an Error class to Tango, I ended up deciding that there were very few if any errors that I felt were non-recoverable in every program where they may occur. Ultimately, it seems to make more sense to leave the decision to the programmer to determine recoverability. If the programmer really wants to generate an error intended to be unrecoverable, he can always throw an Object anyway. Sure, Objects can still be caught, but I think few programs actually do this (outside the catch{} statement anyway). Sean
Mar 01 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Sean Kelly wrote:
 == Quote from Yigal Chripun (yigal100 gmail.com)'s article
   
 My point is that errors have the same relation to exceptions. There is
 additional functionality that  exceptions provide ( you can catch them
 and do stuff) which are not part of an error's interface. Errors should
 represent unrecoverable situations where the program must terminate.
 catching an error is, as downs said, a BUG.
     
So the problem then becomes which errors to classify as non-recoverable. Particularly in a systems language like D, I would be hesitant to dictate that almost any errors must necessarily terminate the program. Consider an array bounds error. This is a system-generated error that clearly indicates a program flaw, and yet the program is still in a valid state. The same could be said of other errors as well. When considering adding an Error class to Tango, I ended up deciding that there were very few if any errors that I felt were non-recoverable in every program where they may occur. Ultimately, it seems to make more sense to leave the decision to the programmer to determine recoverability. If the programmer really wants to generate an error intended to be unrecoverable, he can always throw an Object anyway. Sure, Objects can still be caught, but I think few programs actually do this (outside the catch{} statement anyway). Sean
What you describe is already partly solved in Tango. There are callback functions that allow you as a systems programmer to change the default handler for such errors. so you already realized that these errors and asserts shouldn't be handled by a regular try catch block. to address your example: how can you state that a program with an array bounds error is in valid state? on the contrary, the program accessed an area of memory which it didn't supposed to access. either it read garbage data or worse, it wrote at a random memory location possibly overriding other info stored there. i.e. it is not in a valid state. in C that's usually results in a segmentation fault, which needs to be fixed at debug time. in a running program, would you really want to "catch" such an error and try to recover from it? most likely not. I think that downs' solution is a good one where exception inherits from error . thus, I could catch general Exception objects without worries, and if i need to handle errors in a different way, I can change appropriate handlers. By default, however, asserts and unrecoverable errors must terminate the system. they indicate errors in system logic (i.e. bugs) and should be handled while debugging. in production you can't really recover from an error in system logic. also, I think that the global "Exception" class should come with tracing enabled by default. why would anyone need an exception mechanism without tracing? --Yigal
Mar 01 2008
next sibling parent Sean Reque <seanthenewt yahoo.com> writes:
 also, I think that the global "Exception" class should come with tracing
 enabled by default. why would anyone need an exception mechanism without
 tracing?
 
This is an excellent idea! One of my favorite features of D is conditional compilation. With it, you can take advantage of all the high-level debugging tools available in other languages in debug mode, and compile them out in release. And there isn't a debug tool I probably use more than a simple stack trace of where the error occured. Adding this feature to D would make it a lot friendlier to others used to 4th generation languages. In my first class where I learned C, the teacher failed to teach us anything about debuggers. All i knew at the time was Java, and when I first read the words "Segmentation Fault" I stared back with utter horror. Where was my stack trace? Fortunately, since then I learned how to use GDB with the best of them. However, If D provided a default stacktrace of errors in debug mode, then inexperienced systems programmers wouldn't have to go through as much heartache, and the rest of us would save ourselves from having to use a debugger quite so often.
Mar 01 2008
prev sibling parent reply Sean Kelly <sean invisibleduck.org> writes:
== Quote from Yigal Chripun (yigal100 gmail.com)'s article
 Sean Kelly wrote:
 == Quote from Yigal Chripun (yigal100 gmail.com)'s article

 My point is that errors have the same relation to exceptions. There is
 additional functionality that  exceptions provide ( you can catch them
 and do stuff) which are not part of an error's interface. Errors should
 represent unrecoverable situations where the program must terminate.
 catching an error is, as downs said, a BUG.
So the problem then becomes which errors to classify as non-recoverable. Particularly in a systems language like D, I would be hesitant to dictate that almost any errors must necessarily terminate the program. Consider an array bounds error. This is a system-generated error that clearly indicates a program flaw, and yet the program is still in a valid state. The same could be said of other errors as well. When considering adding an Error class to Tango, I ended up deciding that there were very
few if
 any errors that I felt were non-recoverable in every program where they may
occur.  Ultimately, it
seems
 to make more sense to leave the decision to the programmer to determine
recoverability.  If the
 programmer really wants to generate an error intended to be unrecoverable, he
can always throw an
 Object anyway.  Sure, Objects can still be caught, but I think few programs
actually do this (outside
the
 catch{} statement anyway).


 Sean
What you describe is already partly solved in Tango. There are callback functions that allow you as a systems programmer to change the default handler for such errors. so you already realized that these errors and asserts shouldn't be handled by a regular try catch block. to address your example: how can you state that a program with an array bounds error is in valid state? on the contrary, the program accessed an area of memory which it didn't supposed to access. either it read garbage data or worse, it wrote at a random memory location possibly overriding other info stored there. i.e. it is not in a valid state. in C that's usually results in a segmentation fault, which needs to be fixed at debug time. in a running program, would you really want to "catch" such an error and try to recover from it? most likely not.
Assuming that a write actually occurred at that location, if the location were in dynamic memory it would be technically possible to determine whether the written location was an allocated block or if it was an empty/unallocated block. Assuming it's the latter, the program is still in a valid state and may continue. This obviously wouldn't be the norm, but for some applications I might actually consider doing this and recovering if possible.
 I think that downs' solution is a good one where exception inherits from
 error . thus, I could catch general Exception objects without worries,
 and if i need to handle errors in a different way, I can change
 appropriate handlers.
 By default, however, asserts and unrecoverable errors must terminate the
 system. they indicate errors in system logic (i.e. bugs) and should be
 handled while debugging. in production you can't really recover from an
 error in system logic.
I think it depends. Let's say that this is an application that repeatedly polls some external sensor and passes the readings to an event handler. The event handler only accepts a range of values and tests the input parameters against this range within its "in" clause. Let's further assume that it's not crucial for every sample to be processed--say it's a temperature sensor and occasional samples may be thrown out so long as a valid sample is obtained within a specific interval. In this instance, a precondition failure should not cause the system to fail--the system is in a valid and recoverable state, and the bad sample could be thrown out and another obtained. But it's possible that the event code could have come from a third party where this behavior could not be altered, and performing the range check before calling the event may not be possible if this is a virtual call to any one of a collection of events (speaking of which--will we ever get inheritable pre/post-conditions like it says in the spec?).
 also, I think that the global "Exception" class should come with tracing
 enabled by default. why would anyone need an exception mechanism without
 tracing?
It does as of the last Tango release. A third-party plug-is is still required to actually generate the trace, but it's not unlikely that we'll bundle these libraries in a future release. That said, I think there are valid reasons why tracing may not be preferable. For one thing, generating a stack trace causes a noticeable application slow-down, at least with flectioned. Unless the trace will be displayed to the user, there's really no reason to have them. Sean
Mar 02 2008
parent reply Christopher Wright <dhasenan gmail.com> writes:
Sean Kelly wrote:
 It does as of the last Tango release.  A third-party plug-is is still required
to actually generate the trace,
 but it's not unlikely that we'll bundle these libraries in a future release. 
That said, I think there are valid
 reasons why tracing may not be preferable.  For one thing, generating a stack
trace causes a noticeable
 application slow-down, at least with flectioned.  Unless the trace will be
displayed to the user, there's
 really no reason to have them.
 
 
 Sean
It would be really nice if the trace included line numbers rather than addresses. Sure, there's addr2line, but it's rather annoying to have to invoke that, so I usually still have to employ tracing code. But that would be much slower. Could you do some sort of lazy evaluation of stack traces? I think the answer is at best 'not without some help from the compiler', since you can catch and rethrow and call functions in between.
Mar 02 2008
parent Sean Kelly <sean invisibleduck.org> writes:
Christopher Wright wrote:
 
 Could you do some sort of lazy evaluation of stack traces?
Unfortunately not. 'finally' blocks, scope(exit), and scoped object dtors can alter the stack, making a lazy trace unreliable. Sean
Mar 02 2008
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 02/03/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  Errors should
  represent unrecoverable situations
It is important that programs that work in Debug mode should also work in Release mode. For example: an ArrayBoundsException is not thrown in Release mode, therefore, it should not be catchable in Debug mode. However, I don't think that making a distinction between Error and Exception is the way to go here. I think a better way would be for ArrayBoundsException to be defined in Phobos as: debug class ArrayBoundsException : Exception That way, if you try to catch one, it will be a /compile-time/ error when you compile your release build. Beyond that, I don't think there should be a distinction. Provided that the axiom "what works in Debug mode also works in Release mode" holds (assuming the source compiles in both builds), there should be no need to make this artificial distinction. For example, if one were to write an operating system in D (a daunting task, but let's pretend to be ambitious for a moment). I would /not/ want an Error thrown by an application to kill the whole operating system. At some level, it has to catch the Error and shut down the errant program. The same may well be true, on a smaller scale, for applications and plugins - or even just trial routines.
Mar 01 2008
next sibling parent reply downs <default_357-line yahoo.de> writes:
Janice Caron wrote:
 On 02/03/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  Errors should
  represent unrecoverable situations
It is important that programs that work in Debug mode should also work in Release mode. For example: an ArrayBoundsException is not thrown in Release mode, therefore, it should not be catchable in Debug mode. However, I don't think that making a distinction between Error and Exception is the way to go here. I think a better way would be for ArrayBoundsException to be defined in Phobos as: debug class ArrayBoundsException : Exception That way, if you try to catch one, it will be a /compile-time/ error when you compile your release build.
Yeah, but if you simply catch _all_ exceptions, the compiler will never notice the mistake and the program will still crash. I like the idea in principle, but it would probably work better like this debug { class DebugModeException : Object { } // WAS Error class Exception : DebugModeException { } } else { class Exception : Object { } } --feep
Mar 02 2008
next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 02/03/2008, downs <default_357-line yahoo.de> wrote:
<a good idea>

You're right. I like your idea better than mine.
Mar 02 2008
prev sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
downs wrote:
 Janice Caron wrote:
 On 02/03/2008, Yigal Chripun <yigal100 gmail.com> wrote:
  Errors should
  represent unrecoverable situations
It is important that programs that work in Debug mode should also work in Release mode. For example: an ArrayBoundsException is not thrown in Release mode, therefore, it should not be catchable in Debug mode. However, I don't think that making a distinction between Error and Exception is the way to go here. I think a better way would be for ArrayBoundsException to be defined in Phobos as: debug class ArrayBoundsException : Exception That way, if you try to catch one, it will be a /compile-time/ error when you compile your release build.
Yeah, but if you simply catch _all_ exceptions, the compiler will never notice the mistake and the program will still crash. I like the idea in principle, but it would probably work better like this debug { class DebugModeException : Object { } // WAS Error class Exception : DebugModeException { } } else { class Exception : Object { } } --feep
I limes.
Mar 02 2008
prev sibling parent Sean Kelly <sean invisibleduck.org> writes:
== Quote from Janice Caron (caron800 googlemail.com)'s article
 For example, if one were to write an operating system in D (a daunting
 task, but let's pretend to be ambitious for a moment). I would /not/
 want an Error thrown by an application to kill the whole operating
 system. At some level, it has to catch the Error and shut down the
 errant program.
Right. This is what I was thinking of with my comment about D being a systems programming language. Sean
Mar 02 2008