From: smith@ctron.com (Larry Smith)
Newsgroups: comp.lang.modula2
Subject: Re: Why 3 button mice in Oberon?
Date: 29 Sep 92 13:33:07 GMT
Reply-To: smith@ctron.com
Organization: Cabletron Systems, Inc.

In article <gsc.717334211@coombs>, gsc@coombs.anu.edu.au (Sean Case) writes:
>smith@ctron.com (Larry Smith) writes:

>>Someday, I hope to have time to add exceptions to Oberon

>Whatever for?

Because I consider them to be a fundamental control flow device, and
absolutely required to produce safe code.  I don't have time to enter
into a full net.debate about it, so just take it as my humble opinion.
I'll make just this one post.

To make a long story short, without exceptions we are forced to return
error conditions in-line.  I've experimented with other mechanisms in
Modula-2 - setting exception handlers, etc., but they are unsatisfactory,
you still have to do _something_ at the point of the original call.  In-line
returns have a bunch of problems, they force a widening of the return
type (which is why getchar() in Unix returns an int of all things) and
they are notoriously often ignored.  When they are (not "if"), going back
to add safety becomes a major problem, too, since you have to juggle
working code, potentially damaging the program, while you add the error
checks you shouldn't have omitted in the first place.  Even if you use
exception handler procedures and have them set booleans or something,
you still must check them, although you can now, at least, have your
result be the correct type.

To pick on C:

  int c;
  char *buf = <some buffer>;

  while(c=getchar() != EOF) {
    *buf++ = (char) c;
  }


This piece of code has two problems.  First, the getchar procedure (macro
in this case) is supposed to return a _char_, it instead returns an int,
an artificial widening of the result type to allow for error return values.
This means the result must be cast, an inherently unsafe procedure, to say
the least, before it can be assigned.  Furthermore, the possibility of an
actual error while processing is completely ignored (which is, of course,
tradition in Unix), which means the program will stumble on, casting the
error return values into chars and stuffing them into the buffer until it
is finally too sick to go on.

With exceptions:

  char *buf = <some buffer>

  for(;;) {
    *buf++ = getchar();
    when(eof())break;
  }

This is much cleaner.  Now the checking for errors and eof is carried out
in the rts, where is has to be done anyway, and control is automatically
passed to the exception handler when something out of the ordinary happens.
Notice that I did not have to handle the error case explicitly.  If an error
occurs and an exception is thrown, control will automatically terminate this
procedure and look for a handler in the caller's context.  If none existed,
that caller would be terminated, and a handler looked for in the caller's
caller's context, and so forth, until a handler was found, in which case
the error would be handled, or until we ran out of contexts, in which case
the program aborts.  Notice that I no longer need "c" at all.

Also, notice that in order to catch an error at this level, I don't need to
muck with the code, adding if's and else's, I just add one more line after
when(eof()): when(err()).  Simple, straightforward, and requires no editing
of working code.

Notice another feature:

  int i;
  char buf[100];
  

  for(i = 0; ; i++) {
    buf[i] = getchar();
    when(eof() || range())break;
  }

Now even the loop termination check is being done in the rts, an implicit
check on the range of buf's subscript.  In any reasonable language (which
C is not) that range check would always be done anyway, doing it again in
the loop control wastes time on redundant work or requires a more butch
optimizer in the compiler to avoid.

I could go on at some length, but I don't have time.  The basic point I want
to stress is that exceptions _are_ a _basic_ control structure, and their
absence compromises the safety of a language.  Now, various and sundry
techniques could be used in any of the above examples to make them easier
or faster or what have you, but only exceptions provide a clean, straight-
forward, and orthogonal way to handle them all.  But you _do_ need to turn
your brain sideways in your head to grok what they are and how they apply
to everyday programming.  I took years coming up with my impression of what
they are, how they should work, and why.

As far as I know, however, no language provides the mechanism I have in mind,
though Modula-3 comes close: exceptions should be able to take paramaters.
Each exception handler needs to take a formal parameter list, which it can
use to determine just what went wrong and what variables were involved.
This would reduce the number of exception handlers needed and make the ones
remaining more generic.  M3 does this, but the handler cannot take a "normal"
formal parameter list, it's a subset for some wierd reason.

Wirth has it in for exceptions, as he did for strings for too many years.
Despite his brilliance, I think he has more than a couple of very odd blind
spots.  He seems to have finally grokked object-oriented programming - though
it took a detour through type-extensions before someone pointed out, by
developing Oberon-2, just how little futher he needed to go.  Strings are
still a bit of a second-class citizen, and exceptions are right out.  Too bad.
It's one of the few features that Ada got halfway right.

Larry Smith (smith@ctron.com)  No, I don't speak for Cabletron.  Need you ask?
-
How about we all do something reasonable?  I bet that'll confuse 'em!
Newsgroups: comp.lang.modula2