=begin pod

=TITLE Exceptions

=SUBTITLE Using exceptions in Perl 6

Exceptions in Perl 6 are a special kind of object used to signify when
something has gone wrong, for instance, unexpected data was received, a
network connection is no longer available, or a file is missing which was
expected to exist.

All built-in exceptions inherit from L<Exception>, which provides some basic
behavior, such as storing a backtrace and providing an interface for the
backtrace printer.

=head2 Ad-hoc exceptions

Ad-hoc exceptions work just like in traditional Perl 5, one can simply use
C<die> with a message as to what went wrong:

    die "oops, something went wrong";
    #!> oops, something went wrong in block <unit> at /tmp/0IeSX_XSWO:1

=head2 Typed exceptions

Typed exceptions provide more information about the kind of error that
occurred within the exception object itself.  For instance, if while
executing C<.zombie copy> on an object the path C<foo/bar> is unavailable
(and was expected to be available), then one could raise an
L<X::IO::DoesNotExist> exception like so:

    die X::IO::DoesNotExist.new(:path("foo/bar"), :trying("zombie copy"))
    #!> Failed to find 'foo/bar' while trying to do '.zombie copy'
    #!> in block <unit> at /tmp/BgTfZOotgc:1

Note how the object has provided the backtrace with information about what
went wrong so that hopefully the user of the code can find and correct the
issue more easily.

=head2 Catching exceptions

It is possible to handle exceptional circumstances by supplying a C<CATCH> block:

    die X::IO::DoesNotExist.new(:path("foo/bar"), :trying("zombie copy"));
    CATCH {
        when X::IO { say "some kind of IO exception was caught!" }
    }
    #!> some kind of IO exception was caught!

Here, we are saying that if any exception of type C<X::IO> occurs, then the
message C<some kind of IO exception was caught!> will be displayed.

A C<CATCH> block uses smart matching similarly to how C<given/when> smart
matches on options, thus it is possible to catch various categories of
exceptions and handle them appropriately inside a C<when> block.

To handle all exceptions use a C<default> statement.

   CATCH {
        default { 
            say .WHAT.perl, do given .backtrace[0] { .file, .line, .subname }
        }
   }

=head2 C<try> blocks

To contain an exception use a C<try> block. Any exception that is thrown in
such a block will be caught by the implicit C<CATCH> block or a C<CATCH> block
provided by the user. In the latter case, any exception not handled will be 
rethrown.
    
    class E is Exception { method message() { "Just stop already!" } }

    try {
        E.throw.new; # this will be local
        
        say "This won't be said.";
    }

    say "I'm alive!";

    try {
        CATCH {
            when X::AdHoc { .Str.say; .resume }
        }

        die "No, I expect you to DIE Mr. Bond!";

        say "I'm immortal.";

        E.new.throw;

        say "No, you don't!";
    }

Output:

    I'm alive!
    No, I expect you die DIE Mr. Bond!
    I'm immortal.
    Just stop already!
      in block <unit> at exception.p6 line 21

=head2 Throwing exceptions

One can also explicitly throw exceptions via the C<.throw> method on an
C<Exception> object.

This example throws an C<AdHoc> exception, catches it and allows the code
to continue from the point of the exception by calling the C<.resume> method.

    {
        X::AdHoc.new(:payload<foo>).throw;
        "OHAI".say;
        CATCH {
            when X::AdHoc { .resume }
        }
    }
    "OBAI".say;
    #-> OHAI
    #-> OBAI

If the C<CATCH> block doesn't match the exception thrown, then the
exception's payload is passed on to the backtrace printing mechanism.

    {
        X::AdHoc.new(:payload<foo>).throw;
        "OHAI".say;
        CATCH {  }
    }
    "OBAI".say;
    #!> foo
    #!> in block <unit> at /tmp/FbtB60dxVO:1

This example doesn't resume from the point of the exception, however
it continues after the enclosing block, since the exception was caught, and
control continues after the C<CATCH> block.

    {
        X::AdHoc.new(:payload<foo>).throw;
        "OHAI".say;
        CATCH {
            when X::AdHoc { }
        }
    }
    "OBAI".say;
    #-> OBAI

C<throw> can be viewed as the method form of C<die>, just that in this
particular case, the sub and method forms of the routine have different
names.

=head2 Resuming of Exceptions

Exceptions interrupt control flow and divert it away from the statement
following the statement that threw it. Any exception handled by the
user can be resumed and control flow will continue with the statement
following the statement that threw the exception. To do so call the
method C<.resume> on the exception object.

    CATCH { when X::AdHoc { .resume } } # this is step 2

    die "We leave control after this."; # this is step 1

    say "We have continued with control flow."; # this is step 3

=end pod

# vim: expandtab shiftwidth=4 ft=perl6
