-- Miguel Oliveira e Silva IEETA-DETI, University of Aveiro, Portugal
Dear Ulrich,
Internal errors don't need DP; they require DbC.
DP is needed for external errors (or else, the program will not be
reliable). In attachment follows three versions of a simple
Eiffel program to print a list of prime numbers, following the
three error handling approaches for a single external error: a
program argument that is not an integer number.
It is a clear example that one should use DP for those categories
of errors. If an error is not 100% the responsibility of the
program, then other than what is ensured by the type system (in
the example: a string), one cannot assume nothing more (no
postcondition in the argument(1)
string value). This is not the case, but sometimes it may be
convenient to use of assertions to detect external errors. In
such cases, those assertions should never be disable and a proper
rescue clause should be added (to avoid a cryptic error message to
the program's user).
One can only prove the correctness (reliability, which also adds
robustness, would be a better term) of a program with external
errors iff DP is used.
Of course, if we can prove that a group of internal errors will
never happen, then its associated assertions can safely be
deactivated (no such possibility, by definition, in DP). In
general, no such possibility exists for external errors (unless we
have external tools that ensure their nonexistence [1]).
Regarding the problem of dealing with possible (but very rare) external errors, one may, in the final product version, implement at the convenient level (execution topmost classes/routines) proper rescue clauses to "catch" such exceptions and provide more friendly error messages to the user (eventually with suggestions to solve the problem, or simple to request user feedback for future versions).
Perhaps it would make sense adding to the library default rescue
handlers to provide such a common generic implementations (my
apologies if it already exists).
-miguel
[1] Such as ensure that a program execution is guarded by a
previous execution of a checker that ensures the desired
postconditions attached to an external error. But then again, the
program does not know that, so DP is still required to ensure
reliability.
Who defines the "happy path" of the code?
Is it a program execution outside error handling? A
non-exceptional execution in languages supporting exceptions? A
case-by-case scenario? (In DbC, if I understand correctly
"happiness", is simply a program running without assertion
failures.)
When in "unhappy" state, how does the code separates a(n implicit) precondition failure (client's responsibility), from other failures (supplier)? This distinction is absolutely critical for tracking and handling errors. I suspect that, without DbC, such decision will be judged outside the program by its programmers.
In DP there is no clear distinction between normal "correct" code, and code used for internal error handling. Also, different classes/routines will most likely use different error codes/mechanisms for each source of error, so probably there won't be a systematic way of dealing with them, making error handling even more complex (and ad hoc).
No such problems and ambiguities exist with DbC. In DbC the answer is clear and very simple (to the point, as I have already mentioned, that the program itself knows if it is being executed "correctly" [1], without requiring an external -- human or automatic -- judge). Of course there is no magic involved here, such result is achieved because contracts are redundant to the code [2]. To my best knowledge, in engineering redundancy serves only two positive purposes: fault detection, and fault tolerance [3]. Redundant contracts allow achieving both purposes (assertions to detect failures; and redundant algorithms/classes within proper rescue clauses to approach -- with extraordinary simplicity, and no ambiguity -- fault tolerance).
--
(Am I missing something, or "stable software" is simply being used to mean reliable software (correct & robust)?)
--
One last argument. I think that exceptions, or any other error
handling mechanism, only amplifies the malfunction of a program if
they make more difficult, or even prevents, error handling. That
is the case if it obscures, or even hides, the source of errors.
Such a problematic thing does not happen with DbC and a
disciplined exception mechanism [4]. As I've showed before, such
problem may exist with try-catch alike exceptions, but also with
DP (because there is no clear difference between precondition
error from the remaining errors). In fact, we may define DP as a
no-precondition approach to programming (accepts everything).
IMHO, that's nonsense (except, of course, for external errors).
-miguel
[1] Always considering the existing expressed contracts (hence the "").[2] The exception being concurrent contracts. Those need to be implemented as conditional synchronization points, thus, they cannot be ignored.
[3] All other uses of redundancy are, most likely, bad (and ugly). In fact, I strongly suspect, that we probably can correlate many of programming language history and evolution with a reduced redundancy in their new mechanisms and methodologies.
[4] In a fault-tolerant program we may even choose the level at which we want redundancy -- close to each possible failure (in general, not a good idea) -- or at an upper more abstract level; in either case to goal is always to ensure the respective postcondition&invariant.
To view this discussion visit https://groups.google.com/d/msgid/eiffel-users/2DA5A11E-54F8-41AD-93B5-0A47C40DFDD2%40gmail.com.