On Wed, 3 Mar 1999, Chris Antos wrote:

> >This is one function that needs much better documentation. Doesn't it also
> >skip secret records?
> 
> i agree that many functions need improved docs (i have not yet unzipped the
> 3.1 docs, but it sounds like they're better, though).
> 
> however, if the return type of a function is Err, it should be very obvious
> that you need to check for error returns.  (it reminds me of the fine print
> on certain items these days, such as "do not use this lawn mower to trim
> your hedges.  serious bodily injury may occur."  you'd think some things
> would go without saying, but not always so).

Er, well, sort-of. There's this old principle of not handling errors if
you don't know what to do about them. I.e., if MemHandleNew turns
ErrBodilyInjuryDueToLawnMower, the OS shouldn't require your program to
set up the modem to call 911.

[Disclaimer: the following discussion gets into a lot of
theory-of-programming, and makes some suggestions that are not directly
applicable to PalmOS. Please check return values in your Palm
applications. Do not take internally, do not mix with alcohol, and see
your doctor if any side-effects occur.]

Logically it is necessary to check the error result of every function that
can return an error, but if there is nothing you can actually do about any
of them, there may not actually be any benefit. Quite possibly your code
will crash due to the error in any case, making the difference between
generating an error dialog and crashing (letting the OS generate an error
dialog for you) fairly small. OK, perhaps you can deal properly with the
OS telling you that your file is "not a typewriter", but are you going to
guard against exit() failing to exit? (Which produces a non-halting
conundrum that Turing would be proud of.)

Sound a bit harsh and contrary to suggested practice? Well, perhaps, but
it was just this problem that C++ exceptions (along with all sorts of
other mechanisms in a variety of other languages) were intended to solve. 

Any environment that insists on return an arbitrary return code for each
operation (C, POSIX, PalmOS, Win32, etc.) will cause these sorts of
problems, which, IMO, can be traced back to a fundamental mistake that is
still made, the failure to notice the difference between an Exception and
a Result. An Exception is something that you don't expect to every happen
during "normal operation". The OS may expect it, but that's because the OS
_has_ to expect everything. It shouldn't be your job to expect it. On the
other hand, you have Results, which _are_ expected, even if a particular
one may not be statistically very common. 

For example, consider a function to read a character from a file. Files
have finite length, and most people will expect files to end at some
point. That means that an EOF is _not_ an exception, and the exception
generation mechanism should not be used for reporting an EOF. It should be
treated as a possible return value, just like the bytes from 0 to 255.

Programmers differ (of course) on whether the failure of a memory
allocation is an Exception or a Result. I'll argue for Result, but then I
like to argue.

The error return mechanism I prefer (and I must admit I'm speaking through
my hat on this one, since I've written just as much quick-and-dirty
error-checking-less C code as anyone here) is using the return value for
Results, and supplying some sort of benign result in the case of an
Exception. A separate mechanism (like errno, I'm afraid, or better yet
ferror()) can be used to check to see whether an Exception has occured and
what specific one it is.  The last ingredient, and the most subtle one, is
that in the case of an Exception, the OS routines will continue to execute
safely, bypassing their internals as necessary. (If you are feeding the
return value of one routine into the argument of another, this may well
happen automatically if the OS routines properly check for NULL arugments
and such.) That way, you only need error checking statements in a few key
places in your code (after allocations, after a file-write loop has
completed, etc.) and don't need to check the output of every single
expression -- the OS will do its best to turn the intervening statements
into no-ops. 

In case you haven't noticed, this is effectively identical to the way IEEE
floating-point math treats the Not-a-Number value: a computation can
generate NaN to indicate an error, and any operation that is given NaN as
a parameter will return it as a result. Spreadsheets borrowed the same
trick (via the @NA expression), and some (but very few, unfortunately) C
functions cope with NULL arguments in a similar manner. 

C++ exceptions are a bit different then this, in that they start out by
assuming that every Exception is absolutely fatal -- except for the ones
that aren't. It's so easy to work with non-fatal Exceptions that
programmers can get lured into the trap of using them as Results, and the
programmer is supposed to make the judgement here. 

There was a very clear example of this years ago, back when Delphi was
first coming out, and one of its announced features was execption
handling. An author (Ray Kanopka, I assume) writing about the new features
described a function that would convert a string to an integer, or, if the
string was not a properly formed integer, generate an exception. 

I pointed out to some folks that this was nearly useless. If the program
knew the string was a perfectly formed integer, it probably already knew
the value of the integer. Since the logical use of the function was to
convert a human-entered string into a value, it would at least be useful
to find out how _much_ of the string was a valid integer, and maybe even
chop off the bit that wasn't so that the program could continue to parse
it. By generating an exception, you are basically stating that a mistyped
number is something bizzare, and an excuse for the entire program to crash
(maybe a _polite_ crash, with a friendly dialog box and a comforting
message, but still a crash). 

As it goes, while this could be useful in some situations (parsing a
string you _know_ is a number, and can't think of anything sensible to do
if it isn't), it just isn't particularly useful in _most_ situations.

As it turned out, somebody at Borland realized the same thing, because
when Delphi arrived it turned out to have _two_ numeric parsing fucntions,
one of which returned an exception if the argument wasn't a perfect
integer, with the other one simply returning the location of the first
character that wasn't a digit. 

> also, don't ALL functions ignore secret records if they are currently being
> hidden?

No. Some do, some don't.

-- 
Kenneth Albanowski ([EMAIL PROTECTED], CIS: 70705,126)





Reply via email to