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.

Reply via email to