Exception/Error division in D

Jens Mueller jens.k.mueller at gmx.de
Wed May 30 08:47:34 PDT 2012


Steven Schveighoffer wrote:
> On Wed, 30 May 2012 05:32:00 -0400, Don Clugston <dac at nospam.com> wrote:
> 
> >On 30/05/12 10:40, Jonathan M Davis wrote:
> >>On Wednesday, May 30, 2012 10:26:36 deadalnix wrote:
> >>>The fact that error don't trigger scope and everything is nonsensial.
> >>
> >>If an Error is truly unrecoverable (as they're generally
> >>supposed to be), then
> >>what does it matter? Something fatal occured in your program, so it
> >>terminates. Because it's an Error, you can get a stack trace and report
> >>something before the program actually terminates, but continuing
> >>execution
> >>after an Error is considered to be truly _bad_ idea, so in
> >>general, why does
> >>it matter whether scope statements, finally blocks, or destructors get
> >>executed? It's only rarer cases where you're trying to do something like
> >>create a unit test framework on top of assert that you would
> >>need to catch an
> >>Error, and that's questionable enough as it is. In normal
> >>program execution,
> >>an error is fatal, so cleanup is irrelevant and even potentially
> >>dangerous,
> >>because your program is already in an invalid state.
> >
> >That's true for things like segfaults, but in the case of an
> >AssertError, there's no reason to believe that cleanup would cause
> >any damage.
> 
> There's also no reason to assume that orderly cleanup *doesn't*
> cause any damage.  In fact, it's not reasonable to assume
> *anything*.
> 
> Which is the point.  If you want to recover from an error, you have
> to do it manually.  It should be doable, but the default handling
> should not need to be defined (i.e. implementations should be free
> to do whatever they want).
> 
> But there is no reasonable *default* for handling an error that the
> runtime can assume.
> 
> I'd classify errors/exceptions into three categories:
> 
> 1. corruption/segfault -- not recoverable under any reasonable
> circumstances.  Special cases exist (such as a custom paging
> mechanism).
> 2. program invariant errors (i.e. assert errors) --  Recovery is not
> defined by the runtime, so you must do it manually.  Any decision
> the runtime makes will be arbitrary, and could be wrong.
> 3. try/catch exceptions -- these are planned for and *expected* to
> occur because the program cannot control it's environment.  e.g. EOF
> when none was expected.
> 
> The largest problem with the difference between 2 and 3 is the
> actual decision of whether an exceptional case is categorized as 2
> or 3 can be decoupled from the code that decides between them.
> 
> For example:
> 
> double invert(double x)
> {
>    assertOrEnfoce?(x != 0); // which should it be?
>    return 1.0/x;
> }

It's a logic error. Thus,

double invert(double x)
in { assert(x != 0); }
body
{
   return 1.0/x;
}

> case 1:
> 
> void main()
> {
>     writeln(invert(0)); // clearly a program error
> }

Obviously a logic error.

> case 2:
> 
> int main(string[] args)
> {
>    writeln(invert(to!double(args[1])); // clearly a catchable error
> }

This should be
int main(string[] args)
{
   auto arg = to!double(args[1]);
   enforce(arg != 0);
   writeln(invert(arg));
}

The enforce is needed because args[1] is user input. If the programmer
controlled the value of arg and believes arg != 0 always holds then no
enforce would be needed.

Doesn't this make sense?

Jens

PS
For the record, I think (like most) that Errors should like Exceptions
work with scope, etc. The only arguments against is the theoretical
possibility of causing more damage while cleaning up. I say theoretical
because there was no practical example given. It seems that it may cause
more damage but it does not need to. Of course, if damage happens it's
the programmers fault but it's also the programmer's fault if he does
not try to do a graceful shutdown, i.e. closing sockets, sending a crash
report, or similar.


More information about the Digitalmars-d mailing list