[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Exceptions (Was: OO2C problem)
Michael van Acken and I have gone back and forth many times about this very
issue. I have never been completely satisfied with the results, but the
mechanism in module `Exception' does work and is similar to the Modula-2
mechanism. Michael has been very reluctant to introduce any language
changes (justifiably so), and therefore, we both agree that what we have is
about the best we can do right now. Until the language report changes, and
introduces a "standard" exception handling scheme, we are pretty much stuck.
"Mark K. Gardner" <mkgardne@rtsl3.cs.uiuc.edu> wrote:
> *** Now for the controversy ***
>
> The reason why I wanted to use procedure variables in this manner is
> to provide a module in which 1) users can define classes of exceptions
> which can be handled as specific exceptions or as a class, and most
> importantly 2) that the state of the program in the scope of the
> exception is available for examination. What I would like is something
> like
>
> PROCEDURE p (...);
> TYPE
> BadException = POINTER TO BadDesc;
> BadDesc = RECORD(Exception.ExceptionDesc)
> ...
> END;
> WorseException = POINTER TO WorseDesc;
> WorseDesc = RECORD(Exception.ExceptionDesc)
> ...
> END;
> VAR
> a, b ...
> BEGIN
> ...
> TRY
> ...
> CATCH (e : BadException)
> (* use a, b ... to report or fix the problem *)
> CATCH (e : WorseException)
> (* use a, b ... to report or fix the problem *)
> ELSE
> (* use a, b ... to report or fix the problem *)
> END;
> ...
> TRY
> ...
> END;
> END p;
The way you would implement the above using module `Exception' is something
like the following:
MODULE ExceptTest;
IMPORT
Exception, Out;
CONST
BadException = 1;
WorseException = 2;
VAR
src: Exception.Source;
PROCEDURE Counter (c: INTEGER);
BEGIN
Out.String ("Entering counter "); Out.Int (c, 0); Out.Ln;
IF (c = 0) THEN
Exception.RAISE (src, 1, "[ExceptTest] Counter reached zero")
ELSIF (c < 0) THEN
Exception.RAISE (src, 2, "[ExceptTest] Counter less than zero")
ELSE
Counter (c-1)
END;
Out.String ("Leaving counter "); Out.Int (c, 0); Out.Ln
END Counter;
PROCEDURE p ();
(********************************************************************
At this time, there is no provision for extensible exception types.
I can see the use of allowing these, but, why are these declared
local to p()? Does this mean p() would RAISE all exceptions itself
and nowhere else within the enclosing module?
TYPE
BadException = POINTER TO BadDesc;
BadDesc = RECORD(Exception.ExceptionDesc)
...
END;
WorseException = POINTER TO WorseDesc;
WorseDesc = RECORD(Exception.ExceptionDesc)
...
END;
*)
VAR
a, b: INTEGER;
e: Exception.Source;
BEGIN
a := 5;
b := -4;
(* TRY *)
Exception.PUSHCONTEXT (e);
IF (e = NIL) THEN (* normal execution *)
Counter (a);
Counter (b);
ELSE
(* CATCH (e : BadException) *)
IF (Exception.CurrentNumber(e) = BadException) THEN
Out.String ("Caught bad exception"); Out.Ln;
Exception.ACKNOWLEDGE
(* CATCH (e : WorseException) *)
ELSIF (Exception.CurrentNumber(e) = WorseException) THEN
Out.String ("Caught worse exception"); Out.Ln;
Exception.ACKNOWLEDGE
ELSE
(* use a, b ... to report or fix the problem *)
END;
END;
Exception.POPCONTEXT; (* clean up or be damned *)
END p;
BEGIN
Exception.AllocateSource (src);
p();
END ExceptTest.
Yes, it is somewhat awkward, ugly, and error prone, but that's about all we
can do without changing the language.
If you want to talk about changing the language, my current suggestion is
for providing more language support for our current mechanism: adding
keywords EXCEPT and RETRY (ala XDS), and possibly a "standard" exception
type that could be extended to build classes of exceptions.
Examples:
(* rewrite of p() from above *)
PROCEDURE p ();
VAR
a, b: INTEGER;
BEGIN
a := 5;
b := -4;
Counter (a);
Counter (b);
EXCEPT
IF (IsCurrentSource(src)) THEN
IF (Exception.CurrentNumber(src) = BadException) THEN
Out.String ("Caught bad exception"); Out.Ln;
RETURN
(* a return implictly ACKNOWLEDGEs the exception *)
ELSIF (Exception.CurrentNumber(src) = WorseException) THEN
Out.String ("Caught worse exception"); Out.Ln;
RETURN
END
ELSE
(* use a, b ... to report or fix the problem *)
END;
END p;
(* Or if module `Exception' provided an extensible exception type, and types
`BadException' and `WorseException' were extended from it.
*)
PROCEDURE p ();
VAR
a, b: INTEGER;
e: Exception.Exception;
BEGIN
a := 5;
b := -4;
Counter (a);
Counter (b);
EXCEPT
e := Exception.GetException();
WITH
e: BadException DO
Out.String ("Caught bad exception"); Out.Ln;
RETURN
| e: WorseException DO
Out.String ("Caught worse exception"); Out.Ln;
RETURN
ELSE
(* use a, b ... to report or fix the problem *)
END;
END p;
The mechanism proposed by Mossenbock, et. al., is slick, but I have a
problem with it. There is no real difference between a regular local
procedure and an exception handler. This could cause confusion, especially
if someone used odd naming conventions.
Example:
MODULE X;
...
TYPE
BadName = POINTER TO BadNameDesc;
BadNameDesc = RECORD(Exception.ExceptionDesc)
...
END;
PROCEDURE Y;
PROCEDURE Z(b: BadName); (* It's not obvious that Z is an exception
handler *)
...
END Z;
...
END Y;
I think it would also confuse newcomers to the language and anyone not
familiar with metaprogramming. (We could actually probably implement such a
thing without metaprogramming, but that's beside the point). It just seems
too much like magic unless you really know what is going on. On the other
hand, an explict EXCEPT block is more obvious.
Eric
--
___________________________________________________________________________
Eric Nikitin | When thou wakest, let love forbid
_Into the Realm of Oberon: | Sleep his seat on thy eyelid:
an Introduction to Programming and | So awake when I am gone;
the Oberon-2 Programming Language_ | For I must now to Oberon.
ISBN: 0-387-98279-5 |
http://www.springer-ny.com | -- A Midsummer Night's Dream
___________________________________________________________________________