Failing Gracefully

Most languages have a mechanism to handle failure. For example, ADA has raisewith, C has setjmp(), longjmp(), Python has try:, except:, else:, finally:, and Perl has eval{die}. Despite this, a (library) function will often handle failure in an other way: by return-value. This is because failure comes in many guises.

There are at least two ways the function getsome() could fail in: loop {break if !getsome(ref:D); dosome(D);}. The function getsome() could simply be unable to live up to its name because there is no more or it could encounter a serious error, like disc on fire. Combining both mechanisms is quite possible:

try {
    loop {
        break if !getsome(ref:D)
        dosome(D)
    }
} 
catch DOF {
    out "Disc on Fire!"
    halt
}

One type of failure is actually functional in that it is necessary for the code to execute proper (i.e., terminate), the other type of failure you hope your users will never see, but if they do, they should be grateful you took time and effort to catch it early.
Actually, exception handling is exactly that, a mechanism to handle exceptional situations. But the line is blurry, sometimes one type of failure gives rise to an other:

loop {
    break if !getsome(ref:D)
    if ishalf(D) {
        if !getsome(ref:E) raise "Data Corrupted"
        dosome(D+E)
    }
    else dosome(D)
}

In the above example, getsome() returns a success status, i.e., true is good, false is bad. However it could also have returned a failure status, i.e., true is bad and false is good. Mixing the two return status types in one environment can be confusing. Things get more complicated if a function signals success or failure in-band with invalid data values. For an example of complicated failure handling you need not look further than the standard C library function getc(). It returns an unsigned number between 0 and 255 upon success and the constant EOF upon failure. In fact if EOF is returned, the function feof() or ferror() should always be executed to find out if it is a functional failure (i.e., really End Of File) or if it is something exceptional like Disk On Fire. To really properly handle an error a call to clearerr() might also be in order. I guess, bad design lives for ever because some think you can not force refactoring upon programmers. Backwards compatibility at this level? My donkey!
So this raised two questions. First, “How do I stay out of this mess?” and second, “How do I best handle legacy code?” The answer to the former is quite simple: Use out-of-band failure signaling and be consequent in using either success or failure status return. The answer to the latter is more complex. There are no ironclad rules. When confronted with legacy code the following tricks might help. 1. Use wrapper code to hide the legacy code and keep your own code consistant: #define my_getsome(a) (!getsome(a)). 2. Use a properly named variable like ok, or error to catch the return values: loop{if (error = from_yore(ref:A)) break; dosome(A);}. 3. Rename the legacy functions: function getchar_or_error := getc;. But too much adaptation can be a bad thing too. If only all programmers where wise and doing well, coding would be heaven and not this never ending hell.