=head1 TITLE

    Structured Exception Handling Mechanism

=head1 VERSION

    Maintainer: Tony Olekshy <[EMAIL PROTECTED]>
    Date: 18 Aug 2000
    Version: 2 (Draft 2)
    Mailing List: [EMAIL PROTECTED]
    Number: 88

=head1 DRAFT STATUS

Areas of development of this document which are not yet complete
as of this draft are annotated with the --> glyph.

=head1 ABSTRACT

The Encyclopedia of Software Engineering [ESE-1994] says (p.847):

    Inevitably, no matter how carefully a programmer behaves when
    writing a program and no matter how thoroughly its verification
    is carried out, errors remain in the program and program
    excecution may result in a failure.  [...] The programming
    laguage may provide a framework for detecting and then handling
    faults, so that the program either fails gracefully or continues
    to work after some remedial action has been taken to recover
    from the error.  Such a linguistic framework is usually called
    exception handling.

This RFC describes a collection of changes and additions to Perl,
which together support built-in base classes for Exception and
Error objects, and exception and error handling code like this:

    exception 'Error::DB';

    try {
        throw Error::DB "a message", tag => "ABC.1234", ... ;
        }

    catch Error::DB { ... }

    catch Error::DB, Error:IO { ... }

    catch $@->{message} =~ /divide by 0/ { ... }

    catch { ... }

    finally { ... }

Any exceptions that are raised within an enclosing try, catch, trap,
or finally block, where the enclosing block can be located anywhere
up the subroutine call stack, are trapped and processed according to
the semantics described in this RFC.

The new built-in Error base class is designed to be used by Perl for
raising exceptions for failed operators or functions, but this RFC
can be used with the Exception and Error base classes whether or not
that happens.

=head1 DESCRIPTION

 exception 'Error::DB::Foo';

    Makes Error::DB::Foo into a class that inherits from the built-in
    Exception class.

    If the given name matches /::/, something like this happens:

        @Error::DB::Foo::ISA = 'Error::DB';

    If the given name does not match /::/ (say it's just 'DB'), this
    happens instead:

        @DB::ISA = 'Exception';

    This means that every exception class isa Exception, even if
    Exception:: is used at the beginning of its name.

 throw Error::DB "a message", tag => "ABC.1234", ... ;

    Throw is both a class and an instance method of the build-in
    Exception class.  The indirect object syntax is used to make the
    throw imperitive.  As a class method, it is syntactic sugar for:

        die Error::DB->new(

                message => "a message", tag => "ABC.1234", ...);

    As an instance method it is syntactic sugar for copying over
    any values given as arguments, and then effecting C<die $self>.
    This allows C<throw $@> to be used to re-raise exceptions.

    Note that a derived class can override its constructor to
    preprocess the optional arguments, so that (for example) tags
    are parsed out of the message, which allows something like this
    to work for developers who prefer it (such as the author):

        throw MyError "ABC.1234: A message.";

    This also illustrates why the message is a required argument
    to the throw method.  It should not have to be more complicated
    than that to raise an exception of a given type with a given
    annotation, in common use.  One should not have to always add
    "message =>" just for that.

 try { ... } catch EXPR { ... } finally { ... }

    A try statement starts with a block and is followed by zero
    or more catch and/or finally clauses.

    The expression argument of the catch clause is optional.

 catch { ... }

    Traps all exceptions, according to the unwind semantics
    described below.

    It is a syntax error for a catch all clause like this to be
    immediately followed by another catch clause, because that
    would be dead code that could never be executed.

    Otherwise, it is syntactic sugar for catch 1 { ... }.

 catch Error::DB { ... }

    When catch is follwed by a class name, it is syntactic sugar
    for:

        catch $@->isa($string) { ... }

 catch Error::DB, Error:IO { ... }

    When catch is follwed by a comma-seperated list of class names,
    it is syntactic sugar for:

        catch grep { $@->isa($_) } @list { ... }

 catch EXPR { ... }

    Traps exceptions for which the EXPR returns true, when evaluated
    at the time the catch clause is attempted, according to the
    unwinding sematics described below.

    If multiple statements are required to compute the value of the
    EXPR, use this form: catch do { a; b } { ... }

 finally { ... }

    Once the try block is entered, every finally block is guaranteed
    to be entered before the try statement completes, whether or not
    any exceptions have been raised since the try block was entered.

 die

    If argument isa "Exception", raise it as the new exception and
    die in the fashion that Perl 5 does.

    If argument isa "UNIVERSAL", wrap it in a new Exception (using
    the "object" instance variable), and raise that instead.

    Otherwise stringify the argument, wrap it in a new Exception,
    (using the "message" instance variable), and raise that instead.

    The above guarantees that exceptions are Exceptions (because
    this mechanism depends on that), but because of Exception
    stringification, in simple scripts one can still write:

            open F, $file or die "Can't open $file";

    When an exception $e is raised, the following is automatically
    done (if there is no current exception $@ is already undef):

            $e->{link} = $@;  $@ = $e;  

    This provides the mechanism we use to keep track of raised
    exceptions while unwinding as described below.

 $@

    $@ contains the current exception.

    If $@ is not undef when an exception is raised, then after the
    exception is raised $@->{link} points to the previous exception.

    $@ can *only* be set by die, which guarantees $@ isa Exception,
    as described above.

    Unlike eval, $@ is not cleared when a try statement starts.
    However, $@ can be cleared to undef by using C<eval {}>, even
    though $@ = undef is prohibited.

 eval

    No changes relative to Perl 5.

=head2 Syntax

    <exception> := exception <class> ;

    <throw> := throw <X> <string>? , <options>? ;

    <try> := try <block> <clause>* ;

    <clause> := <catch> | <finally>

    <catch> := catch <test> <block>

    <finally> := finally <block>

    <test> := | <classes> | <expr>

    <classes> := <class> | <class> , <classes>

    <X> := <class> | <object>

    <class>     # Unquoted name of a class that inherits from the
                # built-in Exception class, such as Error::IO.

    <object>    # An instance of the built-in Exception class.

    <options> := name => $value
               | name => $value, <options>

    <block> := { ... }  # A Perl code block.

    <expr>      # A Perl expression which is evaluated when
                # the relevent clause is processed.

    <string>    # A Perl expression which is evaluated and
                # stringified.

=head2 Unwinding Semantics

Computer Architecure, Hardware and Software [RYK-1989] says (p.347):

    Three basic design issues concerning exceptions are (1) whether
    or not the exception is permitted to return to the point where
    the exception is taken, (2) how the execution context to be used
    during the the handler's execution is found, and (3) how the
    association of an exception handler with and exception event is
    established.

Perl's behaviour after a C<die> starts call-stack unwinding, as
envisioned by this RFC, is as described by the following rules.

  1. Whenever an exception is raised Perl looks for an enclosing
     try/catch/finally block.

     If such a block is found Perl traps the exception and proceeds
     as per rule 2, otherwise program shutdown is initiated.

  2. The try block's "next" associated trap/catch or finally clause
     is processed according to rules 3 and 4.  When there are no
     more clauses rule 5 is used.

  3. If a trap block returns true (without itself raising an
     exception), its associated catch block is entered.

     If the catch block is entered and it completes without itself
     raising an exception, the current exception is cleared.
     If a trap clause or its catch clause raises an exception, it
     becomes the current exception, but it does not propagate out
     of the clause (at this point).

     If a trap block raises an exception or returns true, then
     whether or not the catch clause raises an exception, any
     succeeding try/catch clauses up to the next finally clause are
     skipped (for the purpose of the "next" iterator in rule 2),

     Processing then continues with rule 2.

  4. When a finally clause is encountered its block is entered.

     If the finally block raises an exception it becomes the current
     exception, but it does not propagate out of the clause (at this
     point).

     Processing continues with rule 2.

  5. After the catch and finally blocks are processed, if there
     is a current exception then it is re-raised and propagated
     as per Rule 1 (beginning above the current try statement in
     the call stack).

     Otherwise the try statement completes normally and Perl
     continues with the statement after the try statement.

=head2 Built-In Exception Class

In addition to the built-in Exception class described below (which
inherits from UNIVERSAL), a built-in Error class is also defined,
which inherits from Exception.

Exceptions raised by the guts of Perl are envisoned by this RFC to
all be instances of classes that inherit from Error.  Instances of
the actual Error class itself are reserved for so-called anonymous
exceptions, for those cases in which one more or less just wants to
say C<throw Error "My message.">, without a lot of extra tokens, and
without getting into higher levels of the taxonomy of exceptions.

On the other hand, new exception classes that inherit directly from
Exception, as opposed to from Error, are assumed to be asking for
more light-weight functionality.  The intent of this RFC is to
provide a place (Exception) in which methods can be stubbed in for
the functionality required by Errors, so that when they are
overridden by Error they work as expected, but when inherited by
other derivatives of Exception, this error-functionality is avoided
and does not otherwise interfer with the requirements of light-
weight exception handling protocols.  The stack-traceback at throw-
time instance variable, for example, probably doesn't make much
sense when one is throwing success, not failure.

Although this Exception/Error partitioning has not yet been taken
advantage of in this RFC, it does provide a good place to help
make exception handling and error handling into almost the same
thing, without adversely affecting the functionality of either.

=head3 Instance Variables

The built-in Exception class reserves all instance variable
and method names matching C</^[_a-z]/>.  The following instance
variables are defined.

tag

    This is a string which module developers can use to assign
    a unique "identifier" to each exception object constructor
    invocation in the module.  A namespace control mechanism
    for the tags is described below.

severity

    This is some sort of "priority" (such as info v/s fatal) on
    which handing can be based.  The details need to be worked
    out.

message

    This is a description of the exception in language intended
    for the "end user".  Potentially sensitive information should
    not be included here.

debug

    This is a place for additional description that is not
    intended for the end user (because it is "too technical"
    or "sensitive").

object

    If the exception is wrapping an object passed to die, said
    object is saved here.

sysmsg

    This a place for the internal exceptions raised by Perl
    to record system information, along the lines of $!.

trace

    A listref containing a snapshot of the call-stack as at the time
    the exception is first raised.  The array contains hashes (one
    per call stack level), each containing one key value pair for
    each snapshot value at that level.  Here are some examples:

            $e->{trace}->[0]->{file}
            $e->{trace}->[0]->{line}
            $e->{trace}->[0]->{sub}

    This snapshot is set up by the "snapshot" method, so that
    derived classes that don't want this overhead can override
    the method.

    The functionality of caller() is used by the snapshot method to
    populate the trace instance variable, but said snapshot has to
    be taken early and held for possible historic debugging use,
    because usually we want to know where we were when things went
    wrong, not where we realized that (and in the later case,
    caller()s output is indeed current).

link

    A scalar containing the previous exception ($@) at the time an
    exception object is constructed.  Tracking this information is
    going to become more important if Perl starts using exceptions
    for error handling.  For example, if an open throws and then a
    calling DB module throws, and then your UI catches it, you are
    (in many cases) going to want to know why the open threw.

=head3 Methods

The built-in Exception base class defines the following methods.

new

    Constructs a new object instance, using its arguments as
    a hash to initialize the instance variables by name.

    The "tag" instance variable is treated specially in order
    to control the namespace for tags, as follows:

        $self->{tag} = (ref $self) .".". $arg{tag};

    This means that the tags provided to exception construcors
    need only be unique across a derived class, which is often
    constrained in such a manner as to make uniqueness relatively
    easy in practice.

throw

    As a class method a new instance is constucted, passing any
    arguments to the constructor.  This object becomes $self, and
    we now have an instance method.

    As an instance method, any arguments are used to update the
    instance variables (unless just constructed), and this method
    effects C<die $self>.

    This method does not return to local flow control.

C<overload '""'> (Stringification)

    Stringification produces a concatentation of various Exception
    object instance variables and certain delimiters.  The details
    are to be worked out, but an example would be:

        ABC.1234 [Warning] Confirmed but not acknowledged.

tag

    This method returns true if

            $self->{tag} eq (ref $self) .".". $_[0]

    This allows us to easily trap by namespace-controlled tag,
    using a form like this:

            catch $@->tag("Foo") { ... }

any

    This method takes a closure argument.  The given closure is
    invoked for each object on the "exception unwinding stack",
    by following the _link fields in Exception objects, starting
    with $self.

    Each such object is passed to the closure as $_[0].  As soon
    as a closure is found which returns true, this method returns
    true.  If no such closure is found, this method returns false.

    An example of the use of this functionality is provided below.

snapshot

    Used internally to generate the "trace" instance variable.
    Designed to be overridden in derived classes for performance
    or extension purposes.

showStack

    This method generates a string formatted version of the
    exception unwinding stack based on following the "link"
    instance variables, and stringifying each such exception
    object.  C<print $@->showStack> produces something like
    this:
            UIM.1234: Can't add a new person to database.
            APP.2345: Can't update Company relationship.
            DBM.4567: Unable to write to Company table.
            IOM.5678: Can't open file ".../company.db".

    showStack takes the following optional parameters.

    label => 1

        If set the formatted exception stack is annotated with
        the classes of the objects therein, as in:

            Error::UI: UIM.1234: Can't add a new person to database.
            MyError:   APP.2345: Can't update Company relationship.
            Error::DB: DBM.4567: Unable to write to Company table.
            Error::IO: IOM.5678: Can't open file ".../company.db".

    trace => 1

        If set the value returned by showStack includes the Perl
        stack traceback using the information from the *last*
        exception in the "link" chain (that is, from the point
        where unwinding first started).  Something like this:

            UIM.1234: Can't add a new person to database.
            APP.2345: Can't update Company relationship.
            DBM.4567: Unable to write to Company table.
            IOM.5678: Can't open file ".../company.db".

            Try::throw called from test-403.pl[7].
            Try::try called from test-403.pl[9].
            Try::try called from test-403.pl[11].
            Try::try called from test-403.pl[14].

    debug => 1

        Annotates entries in the unwind stack with the values
        from the Debug option in the throw statements, if any.
        For example:

            UIM.1234: Can't add a new person to database.
            Debug: Fred Flintstone
            APP.2345: Can't update Company relationship.
            DBM.4567: Unable to write to Company table.
            IOM.5678: Can't open Company file.
            Debug: /foo/bar/company.dat

=head3 Custom Exceptions

In addition to the C<exception 'MyException'> mechanism described
above, custom exception and/or error classes can be created as
follows.

    File MyException.pm:

        package MyException;  @ISA = 'Exception';

        # override and/or extend class here.

    File main.pl:

        use MyException;

        throw MyException ... ;

=head1 EXAMPLES

The first three of these examples cover the most common uses of
try, catch, and finally.

    if (open(*F, ">foo")) { try { ... } finally { close F; } }

        If foo was opened, close it whether or not the try block
        raised an exception.  If try or close raised an exception
        then propagate the exception after attempting to close
        the file.

    try { fragile(); } catch { print $@->showStack; }

        If fragile() raises an exception it is shown on STDOUT.
        No exception is propagated, unless the catch block raises
        an exception.

    try { ... }
    catch Error::IO { ... }
    finally { ... }

        If the try block raises an Error::IO exception then the
        catch block is invoked.  Whether or not try or catch
        raised an exception, the finally block is invoked.

        If try and catch both raised exceptions, or finally raised an
        exception, is it propagated, but if try raised an exception
        and catch didn't then no exception is propagated (because
        the exception was cleanly caught).

Here's another simple example based on the first two examples.

    sub AttemptClosureAfterSuccessfulCandidateFileOpen
    {
        my ($closure, @fileList) = @_; local (*F);
        foreach my $file (@fileList) {
            try { open F, $file; } catch { next; }
            try { &$closure(*F); } finally { close F; }
            return;
            }
        @fileList and throw Error "Can't open any file.";
        }

Most developers will usually only find need for cases like those
shown above.  The following examples can be used when circumstances
merit, but they should be avoided by those looking from maximal
simplicity.  Anything can be done with state variables and nested
simple trys, at least until you run off the right of the page.  The
following examples are provided for your convenience, not as a
requirement to understanding this RFC.

    try { ... }
    catch Error::IO { ... }
    catch Error::DB { ... }
    catch           { ... }

        If the try block raises an exception then: if the first
        catch matches it is invoked, if the second catch matches
        it is invoked, otherwise the third catch is invoked.

        No exception is propagated unless one of the catch
        blocks raises an exception.

    try { ... } catch $@->isa("Foo") and $@->CanBar { ... }

        Derived exception classes can add new instance variables
        and methods to do things like test new predicates.  The
        above form allows these predicates to be used with try.

    try { ... } catch $@->{message} =~ /.../ { ... }

        Any exception object instance variable can be used
        to test whether or not the exeption should be caught.

    try { ... } catch ref $@ =~ /.../ { ... }

        Catches the current exception if has a class name that
        matches the given regular expression!

    try { ... } catch $@->any(sub{ $_[0]->isa("Foo") }) { ... }

        The catch clause is invoked if any exception (that can be
        found by following the link fields of Exception objects)
        isa Foo.

    my ($p, $q);
    try { $p = P->new; $q = Q->new; ... }
    finally { $p and $p->Done; }
    finally { $q and $q->Done; }

        This construct makes sure that if $q is successfully
        constructed, then $q->Done is invoked even if $p->Done
        raises an exception.

    try     { TryToFoo; }
    catch   { TryToHandleFailure; }
    finally { TryToCleanUp; }
    catch   { throw Error "Can't cleanly Foo."; }

        Propagates a new exception if any of the first three blocks
        throws, unless successfully handled.  This can result in
        better information when unwinding is eventually caught, like
        this:

            UIM.1234: Can't add a new person to the database.
            APP.2345: Can't update Company relationship.
            DBM.3456: Trouble processing SQL UPDATE clause.
            DBM.4567: Unable to write to Company table.
            IOM.5678: Can't open file ".../company.db".
            IOM.6789: Access to ".../company.db" denied.

    try {
        $avoidCatches_JustUnwind = predicate();
        }
    catch $avoidCatches_JustUnwind { throw }
    catch { ... }
    finally { ... }

        This construction allows a try to dynamically decide
        to avoid its catch clause and just propagate any
        error that occurrs in the try block (after invoking
        the finally block).

=head1 MOTIVATION

Over the last ten years, the author has come to rely on exception
handling as a relatively robust mechanism for error handling, and
has used exception handling to implement other, non-error related,
non-local flow-control algorithms.  He has developed a relatively
complete implementation of the functionality described herein, in
Perl 5, in the Try.pm module and its associated regression tests
[TRY-2000].

The author's motivation is to help Perl 6 achieve a relatively
robust exception handling mechanism that is suitable for error
handling via exceptions, is still capable enough to handle the
needs of production programs, and is still suitable for light-
weight exceptions that may not involve errors at all.

The Encyclopedia of Software Engineering [ESE-1994] says (p.847):

    Among the features offered by programming languages to
    support exception handling are the following.

     1. The ability to distinguish the normal control flow from
        the exception handling flow to make the structure of the
        program clear.

     2. The ability to detect faults as they occur in the program,
        not only by explicitly raising an exception but also by
        implicitly raising it on account of the run-time environment.
        [...] Both kinds of faults should be handled uniformly.

     3. The ability to transfer control to a programmer-definable
        exception handler when the fault is detected.  The lnguage
        should specify the rules by which this a detected fault is
        bound to its corresponding exception-handling routine.

     4. The ability to specify how control flows after the exception
        handler is executed, i.e., whether one can resume execution
        from the point at which it left off, or whether the program
        should fail.

    Most early programming languages do not provide specific
    features for exception handling, but rather use the normal
    constructs to implement it. [...]  Obviously this and other
    ad hoc methods to not satisfy the requirements listed above.

To this end, new keywords have been deliberately chosen to represent
the new mechanism, in order to make it clear to the developer when
the code is expecting to deal with unwind semantics (rather than
with local flow control).

In addition, the exception handling mechanism propagates exceptions
that are not cleanly caught, which minimizes the chances for the
developer to forget to re-raise uncaught exceptions.  How many of
us check for IO failures after C<print>s?  And if you're writing a
simple program you wouldn't want to, but you would want the program
to shut down after a failure even if you don't check.

Remembering to always check all subroutine and functions for failure
return codes can be difficult, since nothing about the form of the
call, in the source code, indicates whether or not an possible
failure return code is excpected.  And, the exception handling
technique not only works with subroutines and functions, it also
works with operators and with syntax errors.

Although the following code using the new mechanism:

    try     { may_throw_1 }
    catch     may_throw_2   { may_throw_3 }
    finally { may_throw_4 }

can be written in Perl 5 like this:

    eval { may_throw_1 };
    my $unwinding = $@;
    if ($unwinding) {
        my $test = eval { may_throw_2 };
        $@ and $unwinding = $@;
        if ( ! $@ and $test ) {
            eval { may_throw_3 };
            $unwinding = $@;
            }
        }
    eval { may_throw_4 };
    ($unwinding ||= $@) and die $unwinding;

the opportunity for flow-control errors increases.

=head1 CONVERSION

Although the technique of using exception handing for error
handling often seems foreign at first to developers who are not
used to it, many find that it becomes quite natural when four
concepts are kept in mind.

 1. Wherever you previously would have C<return undef> or some
    other special return code, or a pass-by-reference value, to
    indicate the failure of a subroutine or function, instead
    use C<throw Error>.

 2. Wherever you previously would have written

        $x = foo();  defined $x or return undef;

    to propagate an error from a callee back to your caller,
    you can just write $x = foo(); because unhandled exceptions
    automatically propagate.

 3. Wherever you previously would have written the equivalent of

        $x = foo();
        unless (defined $x) {
            # do something about error
            return undef;
            }

    you can now write

        try { $x = foo(); }
        catch {
            # do something about error
            }

 4. Wherever you previously would have ignored an error in order
    to allow you to restore invariants or enforce postconditions,
    and then C<return undef> to propagate the error, like this:

        $x = foo();
        close F;
        defined $x or return undef;

    you can now write

        try     { fragile(); }
        finally { close F;   }

=head1 ISSUES

Object Model

    This RFC is written using the basic Perl 5 concept of an object
    as a reference to a blessed hash containing instance variable
    name-value pairs.  It may need to be modified to account for
    any new basic Perl 6 object model.

Syntax

    If C<catch EXPR { ... }> proves to be difficult for Perl to
    parse, even in the form C<catch EXPR => { ... }> then the form
    C<trap { EXPR } catch { ... }> is a possible alternative (based
    on earlier versions of this RFC).

Lexical Scope

    Some perl6-language-errors discussion has considered the concept
    of having try, catch, and finally blocks automatically share
    lexical scope.

    The author would rather not introduce that complexity into this
    RFC.  It's already hard enough to keep track of lexical scope in
    nested closures; we don't want to add nested combinations of trys
    (possibly containing closures, and so on) into the mixture.

Keyword Names

    RFC 88 only introduces the try, throw, trap, catch, finally,
    and exception keywords, which are all traditionally related
    to exception handling.  Also, "throw" was chosen because of
    its neutral connotation (unlike "fail" for example), because
    exceptions do not necessarily encapsulate a negative.

    New kewords were chosen so that this can be written:

        try {
            try {
                try     { ... }
                finally { ... }
                }
            catch Error1 { ... }
            catch Error2 { ... }
            catch        { ... }
            }
        catch { print $@; }

    instead of cognitively overloading existing keywords in a manner
    like this:

        eval {
            eval {
                eval     { ... }
                continue { ... }
                }
            else {
                switch ($@) {
                    case /First/  { ... }
                    case /Second/ { ... }
                    else          { ... }
                    }
                }
            }
        else { print $@; }

    because the author is of the opinion that overloading else and
    continue with unwind semantics not traditionally associated with
    else and continue can be confusing, especially when intermixed
    with local flow-control forms of else and continue, or when
    an "else throw" is forgotten on a switch that needs to re-throw.

    Some perl6-language-error discussions have suggested leaving out
    the try altogether, as in simply writing C<{ } else { }>.  Yikes!
    The "try" is not for Perl's sake.  It's for the developers's
    sake.  It says, watch out, some sort of non-local flow control
    is going on here.  It signals intent to deal with action at a
    distance (unwinding semantics).  It satisfys the first rule
    listed under Motivation.

Keyword Alternatives

    -->

retry

    There has been some discussion on perl6-language-error about
    the concept of re-entering try blocks on catch, and the
    possibility of using such a mechanism to replace AUTOLOAD.

    The author is of the opinion that in order to do this sort
    of thing properly one should use continuations, which are
    being discussed elsewhere to this RFC.

    The intent of this RFC is to provide a simple yet robust
    exception handling mechanism that is suitable for error
    handling, not for replacing AUTOLOAD.

eval

    The semantics of eval are, "don't unwind unless the user re-dies
    after the eval".  The semantics of try are "unwind after try
    unless any raised exception was cleanly and completely handled".

    In the author's opinion, both should be left in Perl 6.

    This also means you can clear the current exception with eval,
    even though you can't set $@ directly.

Exception Base Class

    The following questions about Exception should be addressed.

    How to extend ivars and control namespace?
    How to extend methods and control namespace?
    Default values for tag and severity?
    How to categorize severity?
    How to arrange the exception class hierarchy for the Perl core?
    How to tag exceptions in the Perl core?
    What assertions should be placed on the instance variables?
    What should stringification return?
    What can be delegated to Error for performance?

System Messages

    Discussions on perl6-language-errors have pointed out that
    Perl 5 has error state variables named $@, $!, $^E, and $?.
    The $anException->{sysmsg} instance variable was designed
    to provide a place to store system error information, but
    perhaps it should be a dictionary?

$SIG{__DIE__}

    The try, catch, and finally clauses localize and undef
    $SIG{__DIE__} before entering their blocks.  This behaviour
    can be removed if $SIG{__DIE__} is removed.

    If $SIG{__DIE__} is not removed, it should be invoked as
    at the phrase "program shutdown is initiated" in Rule 1,
    not at the time an exception is raised.

    If the old functionality is removed, do we want to introduce
    the new functionality anyway, and some way to nest handlers?

Handler

    Many of the refernces on exception handling refer to the
    concept of an exception class having a handler method that
    is invoked after constructing an instance of the class but
    before raising said exception.  Based on whether or not this
    handler returns successfully, the exception may or may not
    in fact be raised.

    Although this is abhorant from the perspective of error
    handling, where we want the constant that "throw does not
    return", it can be a completely reasonable concept for other
    users of Error objects.  Should we define and process a null
    "handler" method in Exception and not override it in Error,
    or not?

Flow Control Abuse

    Some of the reference texts, when discussing exception
    handling, refer to the matter that it may be difficult to
    implement a C<go to> across an unwinding semantics block,
    as in:

            try { open F, $f } catch { next; }

    This matter will have to be referrred to the good folks
    over in internals.

RFC 96

    Should be withdrawn as it is by the same author as this RFC
    and he says it is now covered here.

=head1 IMPACT

Legacy

    It is not the intent of this RFC to interfer with traditional
    Perl scripts; the intent is only to facilitate the availability
    of a more controllable, pragmatic, and yet robust mechanism when
    such is found to be appropriate.

    Nothing in this RFC impacts the tradition of simple Perl scripts.

    C<die "Foo";> continues to work as before.

    There is no need to use try, catch, or finally, and most of the
    cases where you would want you use them takes less source code
    with exceptions than with return code checking, as per the
    CONVERSION section above.

RFC 63: Exception handling syntax proposal.

    --> Impact statement not ready for this draft.

RFC 70: Allow exception-based error-reporting.

    --> Impact statement not ready for this draft.

RFC 80: Exception objects and classes for builtins.

    --> Impact statement not ready for this draft.

Traditional eval, $@, and die functionality.

    $@ is now always wrapped up in an Exception object, unless
    it already isa Exception object.  $@ can only be set by
    die, and cleared by eval.

=head1 ACKNOWLEDGEMENTS

This RFC is based on invaluable support on a number of fronts.

This RFC has been refined with the help of, via the perl6 mailing
lists, contributions in particular from Peter Scott, Chaim Frenkel,
Graham Barr, and Jonathan Scott Duff.

The Perl 5 implementation of Try.pm [TRY-2000] has been used by the
staff of Avra Software Lab Inc. for production code, it has been
debated in presentations at the Software Engineering Research Lab
at the University of Alberta, and it has been discussed on the
perl-friends mailing list.  At one point it was refined based on
the study of Graham Barr's C<Error.pm> module [GBE-1999].

=head1 REFERENCES

ESE-1994: The Encyclopedia of Software Engineering, J.J. Marciniak
          (editor), John Wiley & Sons Inc, 1994.  ISBN 0-471-54002-1

GBE-1999: Graham Barr's C<Error.pm> module.
          http://search.cpan.org/doc/GBARR/Error-0.13/Error.pm

RFC-63:   Exception handling syntax proposal.
          http://tmtowtdi.perl.org/rfc/63.pod

RFC-70:   Allow exception-based error-reporting.
          http://tmtowtdi.perl.org/rfc/70.pod

RFC-80:   Exception objects and classes for builtins.
          http://tmtowtdi.perl.org/rfc/80.pod

RFC-96:   A Base Class for Exception Objects
          http://tmtowtdi.perl.org/rfc/96.pod

RYK-1989: Computer Architecure, Hardware and Software, R.Y.Kain,
          volume 1, Prentice-Hall Inc., 1989.  ISBN 0-13-166752-1

TRY-2000: Try.pm version 1.1.3.6, Avra Software Lab Inc., 2000.
          http://www.avrasoft.com/perl/rfc/try-1136.zip

=head1 REVISIONS

Version 1, 2000-08-08: Based on Avra Software Lab Inc's Try.pm,
redaction 1.1.3.6.

Version 2 (draft 2), 2000-08-18: Reflects changes made based on
discussion in the various perl6-language lists since 2000-08-08.

Reply via email to