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

=head1 TITLE

Exception handling syntax

=head1 VERSION

       Maintainer: Peter Scott <[EMAIL PROTECTED]>
       Date: 8 Aug 2000
       Last-Modified: 14 Aug 2000
       Version: 3
       Mailing List: [EMAIL PROTECTED]
       Number: 63

=head1 ABSTRACT

This RFC proposes an exception handling syntax for Perl 6.  It was
originally adapted from Graham Barr's C<Error.pm> module, but it's
been modified enough since then that he might not want the blame by
association :-)

=head1 DESCRIPTION

An exception handling mechanism is proposed with the following syntax:

      exception EXCEPTION

      try BLOCK catch [EXCEPTION [, ...] ] BLOCK ...
      try BLOCK catch [EXCEPTION [, ...] ] BLOCK continue BLOCK
      try BLOCK continue BLOCK

      throw EXCEPTION [ (attributes) ]

Exceptions thrown in the C<try> block cause immediate termination of
processing of the C<try> block.  Perl then looks through the C<catch>
blocks in order for one which either names the class the exception
belongs to, or which names none, therefore catches all.  If it finds
one, it executes the code in that block.  It then executes any
C<continue> block.  At that point, if the exception had been caught,
processing continues with the next statement; if it was not caught,
it is propagated up to any enclosing C<try> block and the process
repeated.  The exception object is passed into each block in C<$_[0]>.

An uncaught exception with no enclosing C<try> block causes the program
to C<die> with the text in the C<message> attribute of the exception,
which is what the exception stringifies to.

A C<continue> block is a Java C<finally> block; it gets executed after
the C<try> block, after any C<catch> blocks, but before any uncaught
exception is re-thrown.  In this case we were inheriting syntax from
Perl rather than another language.

Note that C<try> is a keyword, not a function.  This is solely so that
a C<;> is not needed at the end of the last block, since so many people
are bound to think it shouldn't be there and forget.

There is debate over whether C<try> should be C<eval> so as not to use
another keyword.  Since it has different semantics (rethrows uncaught
exceptions), it should not be C<eval>; but if this happens, then surely
C<eval BLOCK> should be retired since it is so similar there would be
confusion over what to use for exception handling.  And no-one would
get the semicolons right :-)

While C<try> and C<catch> are new keywords, C<throw> and C<exception>
don't need to be; they're just functions.

=head2 Exceptions

Exceptions are objects belonging to some C<Exception> class.  C<throw>ing
an exception creates the object; therefore, C<EXCEPTION> above is just a
class name.  C<Error.pm> lets you subclass C<Error> to create them;
C<Exception> appears to be a better name for a core functionality.

The C<exception> function is just syntactic sugar for creating a new
exception class;it merely amounts to C<@EXCEPTION::ISA = 'Exception'>.

=head2 Exception classes - rethrowing

RFC 80 proposes standard exception classes and methods for core exceptions.
This RFC doesn't need to repeat those, but it can expound upon the
semantics that exception classes ought to have.  Assume wlog that they
all inherit from a base class, C<Exception>.  Some people think that
exceptions should not be rethrown implicitly.  We can provide a boolean flag
in the class or even the exception object itself to be checked for this:

     sub Exception::IO::implicit_rethrow { 0 }        # One class
     sub Exception::implicit_rethrow     { 0 }        # All classes
     throw Exception::Socket(implicit_rethrow => 0);  # Just this exception

[Note: it had been proposed that this functionality would be provided by a
method C<uncaught_handler> which would be called if an exception wasn't 
caught.
But if that routine decided to rethrow the exception, the C<continue> block
would not get called at the right time.]

=head2 Exception classes - ignoring

Note that we could also make it possible to selectively or globally ignore
exceptions, so that perl continues executing the line after the C<throw>
statement.  Just import a C<ignore> function that takes a class name:

     ignore Exception::IO;     # Ignore all I/O exceptions
     ignore Exception;         # Ignore all exceptions

and when perl sees the C<throw>, it just does nothing.  (Or do it by
overriding a base class method as for C<implicit_rethrow> if you don't want
to put another function in the namespace.)  Since C<throw> and C<die>
should be essentially identical, this would allow any kind of exception to
be ignored, not just the ones that were C<throw>n.  This is not necessarily
a good thing, of course.

=head2 Examples

These examples assume some core exception classes, although the
mechanism can be implemented even if core exception classes aren't.
The nature of exceptions is that they are most useful on lengthy code;
code brief enough to be a readable example also makes a functionally
poor example.

      exception Exception::MyDB;
      sub foo {
          open_database();
          # database-munging code
          throw Exception::MyDB(message => 'permission denied');
          close_database();
          open_network_connection();
          # network munging code
          throw Exception::IO(message => 'timeout');  #  In Socket.pm, perhaps
          close_network_connection();
          }

      try {
          foo();
      } catch Exception::MyDB, Exception::DBI {
          close_database();
          # other stuff
      } catch Exception::IO {
          close_network_connection();
          # other stuff
      } catch {
          my $err = shift;
          warn "Some other kind of error in foo: $err\n";
          throw $err;      # Re-throw exception (just 'cuz we wanted to)
      }

=head1 IMPLEMENTATION

A C<throw> is just a C<die> passed an object that stringifies to the
message you will die with if nothing catches the exception; therefore
users can use code that throws exceptions without using the exception-handling
mechanism.  They should also be able to wrap a C<try> block around code
that doesn't know about exceptions but does call C<die>, and have that
caught as well; perl should turn the C<die> text into an exception object
with the same text, in the base C<Exception> class.

=head2 $SIG{__DIE__}

<$SIG{__DIE__}> needs to be triggered only as a I<last> resort
instead of firing immediately, since it messes with the mind of this and
every other decent exception handling mechanism.  Perhaps it should be axed
altogether.  Please.

=head1 NOTES

This RFC is very similar to RFC 88, which has slightly different syntax
and adds functionality for adding exceptions to a stack.  While this author
considers that to be going a bit too far (he's tried it before and found he
never made use of anything under the top level of the stack), he wouldn't
mind if RFC 88 got adopted instead.

This RFC is attempting to stake out a more minimalist position.  Some people
think it should be even more minimal by using C<eval> instead of C<try>, and
an C<else> instead of multiple C<catch> blocks.  One advantage of their
approach is that you can use an expression for the exception you wish to
catch in a C<switch> block:

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

whereas with this RFC if you want to catch some class of exceptions more
complicated than a list, you have to catch all exceptions and then look
at the exception you got:

     try { ... }
     catch Exception::IO { ... }
     catch {
         my $e = shift;
         if ($e->my_method) { ... }
     }

But if you don't like what you found, you have to rethrow the exception
explicitly if necessary.  This author feels that the cases where people
would want something more complicated than a list of exception classes
to catch is so rare it is not worth spoiling the elegance of the common
solution simply to avoid rethrowing an exception explicitly.  (Note that
some people think the exception should not be rethrown implicitly in the
first place.)

=head1 REFERENCES

Error.pm (C<http://search.cpan.org/doc/GBARR/Error-0.13/Error.pm>)

RFC 80: Exception objects and classes for builtins

RFC 88: Structured Exception Handling Mechanism (Try)

Reply via email to