=begin pod :kind("Type") :subkind("class") :category("exception")

=TITLE class Failure

=SUBTITLE Delayed exception

    class Failure is Nil { }

A C<Failure> is a I<soft> or I<unthrown> L<Exception|/type/Exception>,
usually generated by calling C<&fail>. It acts as a wrapper around an
L<Exception|/type/Exception> object.

Sink (void) context causes a C<Failure> to throw, i.e. turn into a regular
exception. The L<C<use fatal> pragma|/language/pragmas#fatal>
causes this to happen in all contexts within the pragma's
scope. Inside L<C<try>
blocks|/language/exceptions#try_blocks>, C<use fatal>
is automatically set, and you can I<disable> it with C<no fatal>.

That means that Failures are generally only useful in cases of
code that normally would produce an rvalue; Failures are more or less
equivalent to Exceptions in code that will frequently be called in sink
context (i.e., for its side-effects, such as with C<say>).

Similarly, you should generally use C<&fail> only inside code that is
normally expected to return something.

Checking a Failure for truth (with the C<Bool> method) or definedness (with
the C<defined> method) marks the failure as handled, and causes it not to
throw in sink context anymore.

You can call the C<handled> method to check if a failure has been handled.

Calling methods on unhandled failures propagates the failure. The
specification says the result is another C<Failure>; in Rakudo it causes the
failure to throw.

Because a Failure is L<C<Nil>|/type/Nil>, which is undefined, a common idiom
for safely executing code that may fail uses a
L<C<with/else>|/language/control#with_orwith_without> statement:

=begin code
sub may_fail( --> Numeric:D ) {
  my $value = (^10).pick || fail "Zero is unacceptable";
  fail "Odd is also not okay" if $value % 2;
  return $value;
}

with may_fail() -> $value { # defined, so didn't fail
  say "I know $value isn't zero or odd."
} else { # undefined, so failed, and the Failure is the topic
  say "Uh-oh: {.exception.message}."
}
=end code

=head1 Methods

=head2 method new

Defined as:

    method new(Failure:D: $payload --> Failure)

Returns a new C<Failure> instance with the given payload. The latter can be
either an L<Exception|/type/Exception> or a payload for an C<Exception>. A typical payload
would be a C<Str> with an error message. A list of payloads is also accepted.

    my $e = Failure.new(now.DateTime, 'WELP‼');
    say $e;
    CATCH{ default { say .^name, ': ', .Str } }
    # OUTPUT: «X::AdHoc: 2017-09-10T11:56:05.477237ZWELP‼␤»

=head2 method handled

Defined as:

    method handled(Failure:D: --> Bool:D) is rw

Returns C<True> for handled failures, C<False> otherwise.

    sub f() { fail }; my $v = f; say $v.handled; # OUTPUT: «False␤»

The C<handled> method is an
L<lvalue|/language/glossary#index-entry-lvalue>, see L«routine trait
C<is rw>|/type/Routine#trait_is_rw», which means you can also use it
to set the handled state:

    sub f() { fail }
    my $v = f;
    $v.handled = True;
    say $v.handled; # OUTPUT: «True␤»

=head2 method exception

Defined as:

    method exception(Failure:D: --> Exception)

Returns the L<Exception|/type/Exception> object that the failure wraps.

    sub failer() { fail };
    my $failure = failer;
    my $ex = $failure.exception;
    put "$ex.^name(): $ex";
    # OUTPUT: «X::AdHoc: Failed␤»

=head2 method self

Defined as:

    method self(Failure:D: --> Failure:D)

If the invocant is a L<handled|/routine/handled> C<Failure>, returns it as is.
If not handled, throws its L<Exception|/type/Exception>. Since
L<Mu|/type/Mu> type L«provides C<.self>|/type/Mu#method_self» for every
class, calling this method is a handy way to explosively
filter out Failures:

    my $num1 = '♥'.Int;
    # $num1 now contains a Failure object, which may not be desirable

    my $num2 = '♥'.Int.self;
    # .self method call on Failure causes an exception to be thrown

    my $num3 = '42'.Int.self;
    # Int type has a .self method, so here $num3 has `42` in it

    (my $stuff = '♥'.Int).so;
    say $stuff.self; # OUTPUT: «(HANDLED) Cannot convert string to number…»
    # Here, Failure is handled, so .self just returns it as is

=head2 method Bool

Defined as:

    multi method Bool(Failure:D: --> Bool:D)

Returns C<False>, and marks the failure as handled.

    sub f() { fail };
    my $v = f;
    say $v.handled; # OUTPUT: «False␤»
    say $v.Bool;    # OUTPUT: «False␤»
    say $v.handled; # OUTPUT: «True␤»

=head2 method Capture

Defined as:

    method Capture()

Throws C<X::Cannot::Capture> if the invocant is a type object or a L<handled|/routine/handled>
L<Failure|/type/Failure>. Otherwise, throws the invocant's L<exception|/routine/exception>.

=head2 method defined

Defined as:

    multi method defined(Failure:D: --> Bool:D)

Returns C<False> (failures are officially undefined), and marks
the failure as handled.

    sub f() { fail };
    my $v = f;
    say $v.handled; # OUTPUT: «False␤»
    say $v.defined; # OUTPUT: «False␤»
    say $v.handled; # OUTPUT: «True␤»

=head2 sub fail

Defined as:

    multi sub    fail(--> Nil)
    multi sub    fail(*@text)
    multi sub    fail(Exception:U $e  --> Nil )
    multi sub    fail($payload --> Nil)
    multi sub    fail(|cap (*@msg) --> Nil)
    multi sub    fail(Failure:U $f --> Nil)
    multi sub    fail(Failure:D $fail --> Nil)

Exits the calling C<Routine> and returns a L<Failure|/type/Failure> object wrapping the
exception C<$e> - or, for the C<cap> or C<$payload> form, an
L<X::AdHoc|/type/X::AdHoc> exception
constructed from the concatenation of C<@text>. If the caller
activated fatal exceptions via the pragma C<use fatal;>, the exception is
thrown instead of being returned as a C<Failure>.

    # A custom exception defined
    class ForbiddenDirectory is Exception {
        has Str $.name;

        method message { "This directory is forbidden: '$!name'" }
    }

    sub copy-directory-tree ($dir) {
        # We don't allow for non-directories to be copied
        fail "$dir is not a directory" if !$dir.IO.d;
        # We don't allow 'foo' directory to be copied too
        fail ForbiddenDirectory.new(:name($dir)) if $dir eq 'foo';
        # or above can be written in method form as:
        # ForbiddenDirectory.new(:name($dir)).fail if $dir eq 'foo';
        # Do some actual copying here
        ...
    }

    # A Failure with X::AdHoc exception object is returned and
    # assigned, so no throwing Would be thrown without an assignment
    my $result = copy-directory-tree("cat.jpg");
    say $result.exception; # OUTPUT: «cat.jpg is not a directory␤»

    # A Failure with a custom Exception object is returned
    $result = copy-directory-tree('foo');
    say $result.exception; # OUTPUT: «This directory is forbidden: 'foo'␤»

If it's called with a generic C<Failure>, an ad-hoc undefined failure is
thrown; if it's a defined C<Failure>, it will be marked as unhandled.

=begin code
sub re-fail {
    my $x = +"a";
    unless $x.defined {
        $x.handled = True;
        say "Something has failed in \$x ", $x.^name;
        # OUTPUT: «Something has failed in $x Failure»
        fail($x);
        return $x;
    }
}

my $x = re-fail;
say $x.handled; # OUTPUT: «False»
=end code

=end pod

# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6
