On exceptions, errors, and contract violations

Sean Kelly via Digitalmars-d digitalmars-d at puremagic.com
Fri Oct 3 10:40:41 PDT 2014


I finally realized what's been bugging me about thew program 
logic error, airplane vs. server discussion, and rather than have 
it lost in the other thread I thought I'd start a new one.  The 
actual problem driving these discussions is that the vocabulary 
we're using to describe error conditions is too limited.  We 
currently have a binary condition.  Either something is a 
temporary environmental condition, detected at run-time, which 
may disappear simply by retrying the operation, or it is a 
programming logic error which always indicates utter, 
irrecoverable failure.

Setting aside exceptions for the moment, one thing I've realized 
about errors is that in most cases, an API has no idea how 
important its proper function is to the application writer.  If a 
programmer passes out of range arguments to a mathematical 
function, his logic may be faulty, but *we have no idea what this 
means to him*.  The confusion about whether the parameters to a 
library constitute user input is ultimately the result of this 
exact problem--since we have no idea of the importance of our 
library in each application, we cannot dictate how the 
application should react to failures within the library.

A contract has preconditions and postconditions to validate 
different types of errors.  Preconditions validate user input 
(caller error), and postconditions validate resulting state 
(callee error).  If nothing else, proper communication regarding 
which type of error occurred is crucial.  A precondition error 
suggests a logic error in the application, and a postcondition 
error suggests a logic error in the function.  The function 
writer is in the best position to know the implications of a 
postcondition failure, but has no idea what the implications of a 
precondition failure might be.  So it's reasonable to assert that 
not only the type of contract error is important to know what to 
fix, but also to know how to react to the problem.

Another issue is what the error tells us about the locality of 
the failure.  A precondition indicates that the failure simply 
occurred sometime before the precondition was called, while a 
postcondition indicates that the failure occurred within the 
processing of the function.  Invariant failures might indicate 
either, which leads me to think that they are too coarse-grained 
to be of much use.  In general, I would rather know whether the 
data integrity problem was preexisting or whether it occurred as 
the result of my function.  Simply knowing that it exists at all 
is better than nothing, but since we already have the facility 
for a more detailed diagnosis, why use invariants?

Without running on too long, I think the proper response to this 
issue is to create a third subtype of Throwable to indicate 
contract violations, further differentiating between pre and 
postcondition failures.  So we use Exception to represent 
(environmental) errors which may disappear simply from retrying 
the operation (and weirdly, out of memory falls into this 
category, though @nothrow precludes recategorization), Error to 
represent, basically, the things we want to be allowable in 
@nothrow code, and ContractFailure (with children: 
PreconditionFailure, PostconditionFailure, and InvariantFailure) 
to indicate contract violations.  This gets them out from under 
the Exception umbrella in terms of having them accidentally 
discarded, and gives the programmer the facility to handle them 
explicitly.  I'm not entirely sure how they should operate with 
respect to @nothrow, but am leaning towards saying that they 
should be allowable just like Error.

The question then arises, if contract failures are valid under 
@nothrow, should they derive from Error?  I'm inclined to say no, 
because Error indicates something different.  An Error results as 
a natural consequence of code that should be legal everywhere.  
Divide by zero, for instance.  It isn't practical to outlaw 
division in @nothrow code, so a divide by zero error is an Error. 
  But contract violations are something different and I believe 
they deserve their own category.

How does this sound?  It's only the beginnings of an idea so far, 
but I think it's on the right track towards drawing meaningful 
distinctions between error conditions in D.


More information about the Digitalmars-d mailing list