This and other RFCs are available on the web at
  http://dev.perl.org/rfc/

=head1 TITLE

Omnibus Structured Exception/Error Handling Mechanism

=head1 VERSION

    Maintainer: Tony Olekshy <[EMAIL PROTECTED]>
    Date: 08 Aug 2000
    Last Modified: 23 Aug 2000
    Version: 2
    Mailing List: [EMAIL PROTECTED]
    Number: 88

=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
    execution may result in a failure.  [...] The programming
    language 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 a built-in base class for Exception objects,
and exception/error handling code like this:

    exception 'Alarm';

    try {
        throw Alarm "a message", tag => "ABC.1234", ... ;
        }

    catch Alarm => { ... }

    catch Alarm, Error => { ... }

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

    catch { ... }

    finally { ... }

Any exceptions that are raised within an enclosing try, catch, 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 Exception 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 base class whether or not that
happens.

Readers who are not familiar with the technique of using exception
handling to handle errors should refer to the L<CONVERSION> section
of this document first.

It is not the intent of this RFC to interfere 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<eval {die "Can't foo."}; print $@;> continues to work as before.

There is no need to use try, throw, catch, or finally at all, if
one doesn't want to.

This RFC does not require core Perl functions to use exceptions
for signalling errors.

=head1 DEFINITIONS

raise

    An exception is raised to begin call-stack unwinding according
    to the semantics described herein.  This provides for controlled
    non-local flow-control.  This is what C<die> does.

propagate

    The passing of an exception up the call stack for further
    processing is called propagation.  Raising an exception
    starts propagation.  Propagation stops when the exception
    is trapped.

unwinding

    The overall process of handling the propagation of an exception,
    from the point it is raised until the point it is trapped, is
    called unwinding.

trap

    The termination of unwinding for the purpose of attempting
    further processing using local flow-control semantics, is
    called trapping.  This is what C<eval> does.

cleanly caught

    This means the trapper of an exception did not itself raise an
    exception.

exception

    An exception is a collection of informaton about a particular
    non-local goto, captured at raise-time, for use by trap-time
    handling based on said informaton.  This is what C<$@> is.

error

    A fuzzy concept, an error is essentially an exception with a
    negative connotation.  A traditional definition might be, "an
    exception raised to signal an assertion failure that typically
    indicates the inability of an algorithm to complete the request
    it has been given".  Ultimately, whether or not an exception
    is considered to be an error depends on the trapper, not the
    raiser.

=head1 DESCRIPTION

The most common forms of structured exception handling are straight-
forward.  Here they are:

    try { ... } catch { ... }

        Invoke the catch block only if the try block raises an
        exception (aka dies), otherwise, there is nothing to catch.

        Continue unwinding (propagate $@) only if the catch block
        was invoked and it raises an exception too.

        Otherwise, execute the next statement according to local
        flow-control, because either no exception has been raised by
        either block, or one was raised in try but it was cleanly
        caught.

    try { ... } finally { ... }

        Invoke the finally block whether or not the try block raises
        an exception.

        Continue unwinding (propagate $@) if either the try block or
        the finally block raised an exception.

        Otherwise, execute the next statement according to local
        flow control, because no exception has been raised.

    try { ... }
    catch MatchThis => { ... }
    catch MatchThat => { ... }
    catch              { ... } # everything else
    finally { ... }

        In this case if the try raises an exception then the first
        matching catch is invoked, and then whether or not the try
        (or any catch) raised any exception, the finally is invoked.

        Once a catch clause matches, subsequent catch clauses are
        skipped (like elsif/elsif/else).

        This try statement unwinds only if try raises an exception
        and so does the exception's matching catch block, or finally
        raises an exception.  Otherwise no-one raised an exception,
        or it was in the try and it was cleanly caught.

        This means that all exceptions propagate unless they are
        cleanly caught, just as in Perl 5.  To prevent this, use:

            try { fragile(); } catch { } # Go on no matter what.

    try { ... }
    finally { ... }
    finally { ... }

        Do all finallys even if the try block or any finally block
        raises an exception.  Unwind if any block raised an exception.
        See below.

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

        Do the last catch if any of the first three blocks raises an
        exception, unless it was cleanly caught (that is, it was in
        try and the first catch didn't raise an exception).  Unwind
        if any block raised an exception, unless it was the cleanly
        caught one.  See below.

More complicated constructs should be avoided unless the rules in
L<Unwinding Semantics> make sense to you and your target audience.

=head2 Walkthrough

 throw Exception::IO "a message", tag => "ABC.1234", ... ;

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

        die Exception::IO->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, and
    is described below.

    C<try>, C<catch>, and C<finally> blocks should share the same
    lexical scope, in the way that C<while> and C<continue> do.
    This is so that variables defined in the C<try> block can be
    operated on in the other blocks, which allows one to say:

                try   { my $fh = open $file }
                finally { $fh and close $fh }

    Note that C<try> is a keyword, not a function.  This is so
    that a C<;> is not needed at the end of the last block.
    This is because a try/catch/finally now looks more like an
    if/elsif/else, which does not require such a C<;>, than like
    an eval, which does).

 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 => { ... }.

    To signal failure in a catch block, throw an exception.

 catch Exception::DB => { ... }

    When catch is followed by a class name, the catch block is
    invoked only if the current error is an instance of said
    class.  It is syntactic sugar for:

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

 catch Exception::DB, Exception::IO => { ... }

    When catch is followed by a comma-separated list of class names,
    the catch block is invoked only if the current is an instance
    of one of the given classes.  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 semantics described below.

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

    In order to prevent developers from accidentally writing
    C<catch "Exception:DB" => { ... }> (which would be seen as a test
    that is always true, while they almost certainly were looking for a
    class name), the compiler should detect that special literal case
    and issue a compilation warning.

 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 passed a single argument that isa "Exception", raise it as
    the new exception and die in the fashion that Perl 5 does.

    Otherwise, the arguments are stringified and joined with C<''>
    (as in Perl 5), the resulting string is wrapped up in a new
    Exception object (setting the message instance variable to said
    string), and the new Exception object is raised.

    The above guarantees that $@ isa Exception (so developers can
    depend on that when writing short conditional catch expressions),
    but because of Exception object 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, using the variables described below:

            unshift @@, $@ = $e;

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

 $@ and @@

    $@ contains the current exception, and @@ contains the
    current exception stack, as defined above under C<die>.

    $@ and @@ can I<only> be set by die, which guarantees that
    $@ and $@[$i] are instances of Exception, as described above.

    Unlike eval, try does not start by clearing $@.  The
    successful completion of a catch clause clears $@ and @@.

    Tracking @@ 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.

 eval

    Unchanged from Perl 5.

 exception 'Exception::Foo::Bar';

    Makes Exception::Foo::Bar into a class that inherits from
    the built-in Exception class, something like:

        @Exception::Foo::Bar::ISA = 'Exception::Foo';

 exception 'MyError::App::DB::Foo';

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

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

        @MyError::App::DB::Foo::ISA = 'MyError::App::DB';

    and all non-existent parent classes are automatically created as
    inheriting from their parent, or Exception in the tail case.  If
    a parent class is found to exist and not inherit from Exception,
    a run-time error exception is raised.

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

        @Alarm::ISA = 'Exception';

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

    The exception function can also take optional arguments, along
    the lines of

        exception 'Error_DB', isa => "MyError::App";

    which results (after error checking) in something like

        @Error_DB::ISA = 'MyError::App';

    Other options may possibly be given to C<exception> to control
    things like the raise-time stack traceback.

=head2 Examples

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

    try { my $f = open "foo"; ... } finally { $f and close $f; }

        If the open completed successfully (so defining $f),
        C<close $f> is attempted no matter what else happens.

        If anything in the try or finally clauses raises an
        exception, said exception is propagated after attempting
        the finally block.  Otherwise execution continues locally.

        This sort of functionality is commonly required, not just
        with files and close, but (for example) with objects that
        don't want to rely on garbage collection for the invocation
        of cleanup code.

    try { fragile(); } catch { print "$@\n"; }

        If the try block raises an exception, the catch block is
        entered.  No exception is propagated, unless the catch
        block raises an exception (because if it doesn't the
        exception raised by try is considered "cleanly caught").

        In this example, if fragile() raises an exception it's
        shown on STDOUT.  The stringification method of the
        exception object is used to generate the output.  Note
        that C<print $@->show;> can be used to dump the entire
        exception unwind stack, as described elsewhere herein.

    try { ... }
    catch Exception::Foo => { ... }
    finally { ... }

        If the try block raises an Exception::Foo 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, then it is propagated, but if try raised an
        exception and catch and finally didn't, then no exception
        is propagated (because the exception was cleanly caught).

Here's another simple yet quite robust 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;
            }
        throw Exception "Can't open any file.",
               debug => @fileList . " tried.";
        }

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 or
forget a re-throw.  The following examples are provided for your
convenience, not as a requirement to understanding this RFC or to
using its mechanism.

    try { ... }
    catch Exception::Foo => { ... }
    catch Exception::Bar => { ... }
    catch                   { ... }
    finally { ... }

        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.

        The finally block is entered no matter what happens.

        No exception is propagated unless one of the catch
        blocks raises an exception, or the finally block does
        (because otherwise any exception raised by try is
        considered to have been "cleanly caught").

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

        Any exception object instance variable, such as message,
        tag, or severity, can be used to test whether or not the
        exception should be caught.  The result of the stringif-
        ication of $@ can be similarly tested.

        This also allows one to define a convenience subroutine to
        make, for example, the following work:

                catch not &TooSevere => { ... }

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

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

    try { ... } catch grep { $_->isa("Foo") } @@ => { ... }

        The catch clause is invoked only if any exception on
        the exception unwind stack, aka @@, isa Foo.  Note that
        developers of applications that need to search the stack
        a lot can define a simple convenience subroutine to allow

                catch AnyException("Error::IO") => { ... }

        which is an excellent example of the utility of the
        catch <expr> form.

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

        Rather that repeating a long list of exceptions that you
        need to catch in multiple places, this form allows you to
        put them in a list.  Again, in practice, a convenience
        subroutine would probably be defined to hold the list and
        compute the predicate.

    try { ... } catch $@->isa("Foo") && $@->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.
        If polymorphism is desired, use

            catch $@->can("CanBar") && $@->CanBar => { ... }

    try { my $p = P->new; my $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.  Works for opening and closing a
        pair of files too.

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

        Unshifts a new exception onto @@ if any of the first three
        blocks throws, unless successfully handled.  Use of this
        technique at major API entry points can result in the
        availability of 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 Locations table.
            IOM.5678: Can't open file ".../locations.ndx".
            IOM.6789: File ".../locations.ndx" not found.

        The following table shows the details of this construct.
        The key is 1 = succeeds, 0 = fails, x = skipped.

        TryTo  TryTo    TryTo   | Post Finally Throw
         Foo   Handle  CleanUp  |      (Description)
        ------------------------+--------------------------------------
          1      x        1     | No.  We did Foo and cleaned up.
          1      x        0     | Yes. We did Foo but didn't clean up.
          0      1        1     | No.  We didn't Foo but did Handle it,
                                |      and then we cleaned up.
          0      1        0     | Yes. We didn't Foo but did Handle it,
                                |      but then we didn't clean up.
          0      0        1     | Yes. We didn't Foo and didn't Handle
                                |      it, even though we did clean up.
          0      0        0     | Yes. We didn't do anything right!

    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 occurs in the try block (after invoking
        the finally block).

=head2 Syntax

    <exception> := exception <string> <options> ;

    <throw> := throw <E> <message> ;

    <E> := <class> | <object>

    <message> := # empty
               | <string>
               | <string> <options>

    <try> := try <block> <clauses> ;

    <clauses> := # empty
               | <clause> <clauses>

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

    <catch> := catch <block>
             | catch <classes> <comma> <block>
             | catch <expr> <comma> <block>

    <finally> := finally <block>

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

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

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

    <options> := # empty
               | , <string> => <value>
               | , <string> => <value> <options>

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

    <comma> := , | =>

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

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

=head2 Unwinding Semantics

"Computer Architecture, 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.
The "current exception" is the value of C<$@>.

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

     If such a clause 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 catch <expr> 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 and stack are
     cleared.  But if a catch <expr> or a block raises an exception,
     it becomes the current exception, but it does not propagate out
     of the try statement (at this point).

     If a catch <expr> raises an exception or returns true, then
     whether or not the catch block 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).
     This makes sequential catches work like switch/case.

     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 statement (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 current exception and stack are cleared, the try
     statement completes normally, and Perl continues with the
     statement after the try statement.

=head2 Built-In Exception Class

Exceptions raised by the guts of Perl are envisioned by this RFC to
all be instances of derived classes that inherit from Exception (so
the class hierarchy can be used for exception classification).
This is discussed below under L<IMPACT> + RFC 80.

The built-in Exception class and unwinding functionality described
in this RFC can be used whether or not Perl 6 goes to internal
exceptions.

Instances of the actual (non-subclassed) Exception class itself are
used for simple exceptions, for those cases in which one more or
less just wants to say C<throw Exception "My message.">, without a
lot of extra tokens, and without getting into higher levels of the
taxonomy of exceptions.

=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.

message

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

    This instance variable is also used to record the string given
    when C<die "Can't foo."> is used.  Because of stringification
    (described below), C<$@."" eq "Can't foo."> (for some value
    of eq).

tag

    This is a string which package developers can use to assign
    a unique "identifier" to each exception object constructor
    invocation in their package.  A package-based namespace control
    mechanism that helps ensure the uniqueness of tags is described
    below, under the tag and settag methods.  The tag instance
    variable always contains the fully qualified tag value.

debug

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

    For example, in a web application, internal file names might
    be considered sensitive information, but you would still like
    to get the name of the file that couldn't be opened into the
    server log.

object

    If the exception is related to some particular object, or
    the developer wants to create an object and associate other
    exception information via said object, this can be specified
    via:

        throw Exception "...", object => $object;

trace

    This is 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}

    Alternatively, $e->{trace} could be some sort of snapshot object
    thingy.  Similar stuff has been done by the Perl 5 Devel bundle;
    perhaps there should be a Perl 6 RFC for it.

    The functionality of caller() is used to populate this snapshot
    data, 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, I<not> where we were when
    we caught that (by which time, the raise-time stack snapshot
    data is irrecoverable).

    This snapshot data is set up by the "snapshot" method described
    below, so that derived classes that don't want this overhead
    can override that method to do nothing.  This can be important
    to applications that want to use a large number of light-weight
    exceptions to implement non-local success-based flow-control
    gotos, where the cost of taking and maintaining the snapshots
    could prove to be prohibitive, especially since they would
    normally never be used.

    The snapshot method is an overrideable built-in rather than a
    stub though, because in fact in most cases one does want to pay
    the price for being able to debug exceptions, because said price
    is small (in most cases).

severity

    This is some sort of "priority" (such as info v/s fatal) on
    which handing can be based.

    Note that the severity instance variable is properly the province
    of the various "exceptions for built-ins" proposals, such as RFC 80.

sysmsg

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

    Note that the sysmsg instance variable is properly the province
    of the various "exceptions for built-ins" proposals, such as RFC 80.

=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} = $self->settag($arg{tag});

throw

    As a class method a new instance is constructed, 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 (modulo the
    on_raise mechanism described below).

C<overload '""'> (Stringification)

    Stringification produces a concatenation of various Exception
    object instance variables and certain delimiters.  The message
    instance variable is always to be included.  The details are to
    be worked out, but an example would be:

        ABC.1234 [Warning]: Confirmed but not acknowledged.

settag

    This method effects whatever operations are performed on the
    tag short form given in a throw like this

        throw Exception "a message", tag => "My/Tag/Scheme"

    in order to come up with fully qualified value to be stored
    in the tag instance variable.

    These operations are properly the province of RFC 80, but the
    default could be something like this:

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

    or equivalent, as described below.

    This would mean that the tags provided to exception constructors
    need only be unique across a class, file, or package, which is
    often constrained enough to make uniqueness relatively easy in
    practice.

    The example of exception class constructor overriding given
    later in this RFC needs to set the tag, while maintaining
    namespace control, without knowing the namespace control
    details, so this method is provided for that.

    Perhaps all this instance variable accessor method stuff can
    be cleaned up with other changes to the Perl 6 OO mechanism.

tag

    This method returns true if the object's tag instance variable
    matches its argument.  This allows us to easily trap by
    namespace-controlled tag, using a form like this:

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

    The definition of "matches" can be changed, for an exception
    class hierarchy, by overriding this method.  The default rule
    properly is the province of RFC 80, but it could be something
    like this (for unique tags across all throws of the same class):

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

    or for unique tags across all throws in the same package:

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

    or for unique tags across all throws in the same file:

            $self->{tag} eq (caller(0))[1] ."\t".$_[0]

fatal

    Discussions on perl6-language-errors have indicated a desire to be
    able to easily separate "fatal" exception (like divide by zero) from
    "non-fatal" exceptions (such as can't open file) if Perl 6 uses
    exceptions for error signalling.  The C<severity> attribute of
    Exception objects is designed to handle this data.  This C<fatal>
    function computes the predicate, "is my severity fatal?", to make
    it easy for developers to write:

            catch $@->fatal => { handle_fatal_exception(); }

show

    This class and instance method generates a string formatted
    version of the exception unwinding stack based on the contents
    of @@, stringifying each exception object as it goes.  For
    example, C<print $@->show> 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".

    C<show> takes the following optional parameters.

    label => 1

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

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

    trace => 1

        If set, the value returned by C<show> includes the Perl
        stack traceback using the information from the I<last>
        exception in the stack, that is $@[-1].  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

    Developer's who need alternative output can override C<show>.

snapshot

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

on_raise

    Derived classes may override this method to attempt to "handle"
    an exception or otherwise manipulate it, just before it is raised.
    If on_raise throws or does not return true the exception is raised,
    otherwise it is not.

    In the Exception base class this method returns false, so by
    default exceptions are always raised.

    Unraised throws make no sense for error objects and are abhorrent
    to users of exception handling for error handling, but that's ok
    because Exception's on_raise method returns false by default,
    you have to actually override a method to change that behaviour,
    and, well, error objects could use on_raise for instance variable
    munging at propagate-time, as long as they return true.

    Nevertheless, other uses for Exception objects may want this
    functionality, at least based on the reference and text books
    which say that classically, an exception has such a handler
    hook.

    If we do provide it, it need not be used, but if we don't
    provide it the functionality cannot otherwise be provided,
    should it be deemed desirable for some application.

on_catch

    In the name of symmetric hooks, an on_catch method is called just
    before an exception object is cleared from C<@@>.  on_catch may
    be overridden by a derived exception class to perform additional
    debug logging, or (in fact), to refuse the completion of the
    catch (by raising a new exception).

=head3 Custom Exceptions

In addition to the C<exception 'MyException'> mechanism described
above, custom exception and/or error classes can be created along
the following lines:

    File main.pl:

        use Error::App;

        exception 'Error_DB', isa => 'Error::App';

        throw Error_DB "ABC.1234: Can't foo.";

    File Error/App.pm:

        package Error::App;  @Error::App::ISA = 'Error';

        use Error;  # It inherits from Exception.

        sub new
        {
            my ($C, $msg, %opt) = @_;  $C = ref $C || $C;

            my $self = $C->SUPER::new($msg, %opt);

            $self->{message} =~ s/^([A-Z]+\/\d+):\s+//
            and
                $self->settag($1);

            return bless($self, $C);
            }

    Note that the scope of classes like Error::App is is limited to
    packages that use it, which presumably want such functionality.

=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, in Scheme, C++, Visual Objects,
Delphi, and Perl.  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].
Try.pm is used in large production applications.

The authors' 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 language
        should specify the rules by which this 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 have 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 a failure return
code should be expected.  And, the exception handling technique not
only works with subroutines and functions, it works with operators
too (which, you will note, is why divide dies on zero denominator:
it has no other way to return an error code).

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 $exception = $@;
    if ($exception) {
        my $test = eval { may_throw_2 };
        $@ and $exception = $@;
        if ( ! $@ and $test ) {
            eval { may_throw_3 };
            $exception = $@;
            }
        }
    eval { may_throw_4 };
    ($exception ||= $@) and die $exception;

the opportunity for flow-control errors increases.

Without the functionality provided by the mechanisms described
in this RFC, instead of having a way to be able to write

    throw Error_DB "ABC.1234: Can't write to table $table.";

a developer would be I<required> to write something like

    throw Exception::Error::App::DB tag => "ABC.1234",
        message => "Can't write to table $table.";

The latter has a much lower signal to noise ratio than the
former, which is of I<significant> importance to regular users
of exception handling mechanisms.

=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 Exception> (or some fancier form thereof).

 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 C<$x = foo();> because unhandled exceptions
    automatically propagate.

 3. Wherever you previously would have written the equivalent of the
    following to do something about an error, and then ignore it:

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

    you can now write

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

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

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

    you can now write

        open F, ...;
        try     { $x = foo(); }
        finally { close F;    }

    because unhandled exceptions automatically propagate.

If you don't want to use exception objects just don't use C<throw>,
instead just keep on using C<die "string";> as in Perl 5 (because
C<$@> stringifies reasonably).

=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.

New Keywords

    RFC 88 only introduces the try, throw, catch, finally, and
    exception keywords, which are all traditionally related
    to exception handling.  And they don't even need to all be
    keywords, some can be functions (depending on the Perl 6
    parser, but certainly throw is an Exception method, and
    C<exception> can be done with a subroutine).

    New keywords were chosen so that this can be written:

        try {
            try {
                try     { ... }
                finally { ... }
                }
            catch Exception1 => { ... }
            catch Exception2 => { ... }
            }
        catch { print $@, "\n"; }

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

        eval {
            eval {
                eval     { ... }
                continue { ... }
                }
            else {
                switch ($@) {
                    case /Exception1/ { ... }
                    case /Exception2/ { ... }
                    else { die $@; }
                    }
                }
            }
        else { print $@, "\n"; }

    because the authors are 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 (which may
    be present in any { ... } block), or when an "else die $@" 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 developer'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 satisfies the first rule
    listed under L<MOTIVATION>.

    The "throw" verb was chosen for raising an exception, because
    of its neutral connotation (unlike "fail" for example), because
    exceptions do not necessarily encapsulate a negative.  Similarly,
    "catch" is neutral, unlike "try { ... } error { ... }".

Keyword Names

    Much discussion was generated on the perl6-language lists about
    the selection of keywords for this mechanism.  The minimalist
    approach of cognitively overloading existing keywords such as
    eval, else, and continue has been discussed above.

    The try / throw / catch / finally combination works well in
    consideration of the other requirements discussed herein.

    For the record, here are some of other words that have either
    been used by other programming languages, or suggested on the
    mailing lists: raise, always, onException, when, signal, fail,
    handle, otherwise, unwind, trap, quit, trip, deal, freak, panic,
    cope, punt, ascend, cough, sustain, overrule, and (of course)
    longjmp.

Syntax

    The comma or => in the catch clause is required so the
    expression can be parsed from the block, in the fashion
    of Perl 5's C<map expression, list>;

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, "clear $@ and 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 which case clear $@".

    In the author's opinion, both eval and try should exist in Perl
    6.  This would also mean that the legacy of examples of how to
    use eval in Perl will still work.  The co-author is not so sure.

    And, of course, we still need C<eval $string>.

    Discussions on perl6-language-errors have shown that some would
    prefer the eval { ... } form to be removed from Perl 6, because
    having two exception handling methods in Perl could be confusing
    to developers.  This would in fact be possible, since the same
    effect can be achieved with:

            try { } catch { } # Clears $@.

            my $e;

            try { ... } catch { $e = $@; }

            # now process $e instead of $@

catch v/s else + switch

    Some participants in discussions on perl6-language-errors have
    expressed the opinion that not only should C<eval> be used
    instead of C<try>, but C<else> should be used instead of
    multiple C<catch> blocks.  They are of the opinion that an
    else { switch ... } should be used to handle multiple catch
    clauses.

            eval { ... }
            else {
                switch ($@) {
                    case $@->isa("Exception::IO") { ... }
                    case $@->my_method { ... }
                    }
                }

    This problem with else { switch ... } is: how should the code
    implicitly rethrow uncaught exceptions?  Many proponents of this
    model think that uncaught exceptions should not be implicitly
    rethrown; one suggests that the programmer should C<undef $@>
    at the end of I<every> successful <case> block, so that Perl
    re-raises any C<$@> still extant at the end of the C<else>.

    This RFC allows a switch to be used in a catch { ... } clause,
    for cases where that approach would minimize redundant code in
    catch <expr> { ... } clauses, but with the mechanism proposed in
    this RFC, the switch functionality shown above can be written
    like this, while still maintaining the automatic exception
    propagation when no cases match:

            try { ... }
            catch Exception::IO => { ... }
            catch $@->my_method => { ... }

Exception Base Class

    If Exception objects are not to be used for signaling assertion
    failures by the guts of Perl 6, this RFC stands alone.  Otherwise,
    this RFC delegates the details of the Exception class to RFC 80,
    modulo the required functionality described herein (more or less,
    that internal exception objects are instances of classes that
    inherit from the built-in Exception class).

    Either way, the following questions about Exception should be
    addressed.

    How can we subclass Exception and control the class namespace?
    For example, if the core can use any Exception::Foo, where does
    one connect non-core Exceptions into the taxonomy?  Possibly
    the core exceptions can derive from Exception::CORE, and everyone
    else can use the Exception::MyPackage convention.

    How can we add new instance variables and methods to classes
    derived from Exception and control those namespaces?  Perhaps
    this will be covered by some new Perl 6 object technology.
    Otherwise, we will need yet another naming scheme convention.

    What should the default values be for Exception object instance
    variables not specified to the constructor?  For example, tag
    could default to file + line number.

    What assertions should be placed on the instance variables, if
    any?

    What should stringification return?

on_raise and on_catch

    Some participants in discussions on perl6-language-errors
    currently prefer that the functionality represented by
    the on_raise method of the Exception class not be include at
    all.  Should that be deemed to be the case, the on_raise
    method of the Exception class can be left out.

    It has also been mentioned that we should consider making
    overriding of throw impossible, so that throw must die.

    The author is of the opinion that these constraints should not
    be enforced, because (1) they may provide useful functionality
    for some applications, (2) you have to override a method, which
    is fairly hard to do by accident, and (3) if this functionality
    is not built in, there is no way to simulate it.  The co-author
    is not so sure.

    Independent of the matter of throw and the hook methods, the
    authors agree that there should be I<no way> to disable the
    raising of exceptions on some sort of "global" level.  They
    get through unless they, or their handling, explicitly say
    otherwise.

Lexical Scope

    The authors would prefer that try, catch, and finally blocks
    share the same lexical scope.

    If it is not possible to have try, catch, and finally blocks
    share lexical scope (due, perhaps, to the vagaries of stack
    unwinding), this feature can simply be deleted, and the outer
    scope can be shared.

    One possible problem is illustrated by this:

        try { fragile(); }
        catch { my $caught = 1; }
        finally { $caught and ... }

    If fragile() doesn't throw then finally is going to test
    $caught even though the my statement was never executed.

    These matters will have to be referred to the internals
    experts.

Mixed Flow Control

    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 referred to the internals experts.
    It's ok if this functionality is not possible, it can always be
    simulated with lexical state variables instead.

Core Functionality

    If the guts of Perl 6 are to use exceptions for errors, some
    of the stuff in this RFC has to go into the guts of Perl 6.

    On the other hand, if Perl 6 decides to stay with just eval,
    die, and $@, then the functionality of this RFC can still
    be provided by a (core) module, along the lines of that
    implemented (with different syntax), in Perl 5, in [TRY-2000].
    The actual syntax used would then depend on what parser
    extensions are available in Perl 6.

    The authors are of the opinion that the public interface to
    to the core Perl API should support but not require the use
    of exceptions for errors, under the control of a C<use fatal;>
    or C<use strict 'fatal';> pragma.

Stack Snapshot Object

    This RFC mentions that an exception's C<trace> instance variable
    could point to a "stack snapshot" object, rather than to a data
    structure.  A mechanism like this is available in the Perl 5
    Devel bundle.  If an RFC for such a snapshot class is added to
    Perl 6, this RFC should be reconciled with it.

Mechanism Hooks

    In the name of extensibility and debugging, there should be
    hooks for callbacks to be invoked when a try, catch, or finally
    block is entered or exited, and when a conditional catch is
    evaluated.  The callbacks would be passed information about
    what is happening in the context they are being called from.

    In order to scope the effect of the callbacks (rather than
    making them global), it is proposed that the callbacks be
    specified as options to the try statement, something like
    this:

        try on_catch_enter => sub { ... },
            on_catch_exit  => sub { ... },
        {
            ...
            }

    The (dynamic, not lexical) scope of these callbacks is from
    their try down through all trys nested under it (until
    overridden at a lower level).  Nested callbacks should have a
    way of chaining to callbacks there were in scope when they come
    into scope, perhaps by including a reference to the outer-scope
    callback as a parameter to the callback.

    Normally, this mechanism would only be used by an application's
    top-level try statement, typically for the purpose of debugging
    non-local flow control, or extending the semantics of try.

    For example, to refuse to allow any code under the scope of a
    try to catch a given error, one could just say:

        on_catch_enter => sub { $@->isa("Alarm") and throw $@ }

    The author would find this functionality very useful in an
    application he works with that uses non-error exceptions
    to implement non-local success-based flow control in a MVC
    architecture.

$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 by C<die>.

    If the old functionality is removed, do we want to introduce
    such functionality to Rule 1 anyway?

=head1 IMPACT

Legacy

    The only changes in re Perl 5 behaviour are that (1) $@ is now
    always an Exception object (which stringifies reasonably), it
    is now read-only, and it can only be set via C<die>, (2) the
    @@ array is now special, and it is now read-only too.

RFC 63: Exception handling syntax proposal.

    The author of RFC 63 (and co-author of RFC 88) is currently
    considering withdrawing RFC 63.

RFC 70: Allow exception-based error-reporting.

    RFC 88 is proposed whether or not faults in the Perl 6 guts
    are signaled by exceptions, and whether or not such behaviour
    is under the control of a C<use fatal;>.

RFC 80: Exception objects and classes for built-ins.

    The author of RFC 80 is the co-author of RFC 88, and so is
    aware of the impact of RFC 88 on RFC 80.

    RFC 88 proposes that it's Exception and RFC 80's Exception be
    the same thing.  RFC 88 talks about the participation of an
    Exception in exception handling.  RFC 80 talks about how to use
    an Exception object to signal an exception in Perl's built-in
    functions and other guts.

RFC 96: A Base Class for Exception Objects

    The author of RFC 96 and RFC 88 is withdrawing RFC 96.

RFC-119: Object neutral error handling via exceptions.

    RFC 119 is a proposal for forms of functionality similar to
    those proposed in RFC 88.

RFC-140: One Should Not Get Away With Ignoring System Call Errors 

    The functionality in RFC 140 is envisioned for use in cases
    that don't want to be running <use fatal;>.  It is orthogonal
    to RFC 88 whether or not running <use fatal;>.

=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 from Graham Barr, Chaim Frenkel, Jonathan Scott
Duff, Glenn Lindermann, Dave Rolsky, and Corwin Brust.  It has also
benefited from conversations with Jim Hoover.

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 AUTHORS

Tony Olekshy <[EMAIL PROTECTED]> is the principal author of this
RFC, and is responsible for any errors contained herein.

Peter Scott <[EMAIL PROTECTED]> is co-author of this RFC, by virtue of
the major contribution he has made hereto, and his permission to be
listed as such.

Where this document refers to the singular author or author's, it
refers to Tony.  Where it refers to co-author or co-author's, it
refers to Peter.  Where it refers to the plural authors or authors',
it refers to both authors.

=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 built-ins.
          http://tmtowtdi.perl.org/rfc/80.pod

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

RFC-119:  Object neutral error handling via exceptions.
          http://tmtowtdi.perl.org/rfc/119.pod

RFC-140:  One Should Not Get Away With Ignoring System Call Errors 
          http://tmtowtdi.perl.org/rfc/140.pod

RYK-1989: Computer Architecture, 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 [TRY-2000].

Version 2, 2000-08-23

 1. The "except" keyword is gone, "catch" takes optional
    arguments instead.

 2. Multiple conditional catch clauses now work like a switch,
    instead of like a bunch of sequential ifs.

 3. A built-in Exception-based Error class is no longer defined.

 4. Other major restructuring and enhancements throughout.


Reply via email to