NAME
    results - why throw exceptions when you can return them?

SYNOPSIS
      use results;
  
      sub to_uppercase {
        my $str = shift;
    
        return err( "Cannot uppercase a reference." ) if ref $str;
        return err( "Cannot uppercase undef." ) if not defined $str;
    
        return ok( uc $str );
      }
  
      my $got = to_uppercase( "hello world" )->unwrap();

DESCRIPTION
    This module is a Perl implementation of Rust's standard error handling
    mechanism. Rust doesn't have a `try`/`catch`/`throw` mechanism for
    throwing errors. Instead, functions can be declared as returning a
    "Result" which may be an "Ok" result or an "Err" result. Callers of these
    functions will get a compile-time error if they do not inspect the result
    and potentially deal with the error. (There is syntactic sugar for
    propagating the error further up the call stack.)

    Recent versions of Perl provide `try`/`catch`/`throw` (though `throw` is
    spelled "die"), and in older versions the same thing can be roughly
    accomplished using `eval` or CPAN modules, making Rust's error handling
    seem fairly foreign. For this reason I do not recommend using the a
    mixture of `try`/`catch`/`throw` error handling and Result-based error
    handling in the same codebase. Pick one or the other.

    Result-based error handling can provide some pretty succinct idioms, so I
    do think it is worthy of consideration.

RETURNING RESULTS
  Introduction
    If you decide that your function should return a Result object, your
    function should *always* return a Result object.

    Do not return Results for errors but bare values for success. The
    following example is bad because the caller cannot rely on the result of
    `to_uppercase` *always* being a Result object.

      use results;
  
      sub to_uppercase {
        my $str = shift;
    
        return err( "Cannot uppercase a reference." ) if ref $str;
        return err( "Cannot uppercase undef." ) if not defined $str;
    
        return uc $str;  # BAD
      }

    Instead:

      use results;
  
      sub to_uppercase {
        my $str = shift;
    
        return err( "Cannot uppercase a reference." ) if ref $str;
        return err( "Cannot uppercase undef." ) if not defined $str;
    
        return ok( uc $str );  # FIXED
      }

    `die` can still be used in code that uses result-based error handling, but
    it should only be used for errors that are thought to be unrecoverable.
    Don't expect your caller to `catch` exceptions.

  Functions
    The results module provides three functions used to return results. These
    functions should nearly always be prefixed with Perl's `return` keyword.

   `ok()`
    The `ok()` function returns a successful result. It can be called without
    arguments to represent success without any particular value to return.

      return ok();    # success

    You may also include a value:

      sub your_function () {
        ...;
        return ok( $output );
      }

    Or multiple values:

      sub your_other_function () {
        ...;
        return ok( $count, \@output );
      }

    The caller can then retrieve those values using:

      my $output = your_function()->unwrap();
      my ( $count, $output_ref ) = your_other_function()->unwrap();

    If a list of return values was provided, then calling `unwrap()` in scalar
    context will return the last item on the list only.

   `ok_list()`
    This function acts identically to `ok()` except that calling `unwrap()` in
    scalar context will die.

    Your caller should never need to check at runtime whether it got an `ok()`
    result or an `ok_list()` result. For any given function, you should settle
    on just one of them. `ok()` is usually the best choice as it can still be
    used in list context.

    `ok_list()` is not exported by default, but can be requested:

      use results qw( :default ok_list );

   `err()`
    The `err()` function returns an error, or unsuccessful result.

    It can be called without arguments to represent a general sense of doom,
    but this is usually a bad idea:

      return err();    # failed

    It is generally better to give a reason why your function failed:

      return err( "This feature isn't implemented" );

    Or even better, an exception object:

      return err( MyApp::Error::NotImplemented->new );

    The results::exceptions module provides a very convenient way to create a
    large number of lightweight exception classes suitable for that.

    Like `ok()` this can take a list:

      return err( MyApp::Error::Net->new, 0 .. 99 );

    This would be unusual though, and is not generally recommended.

  Exception Objects
    It is often easier for your caller to deal with exception objects rather
    than string error messages. This module comes with results::exceptions to
    make creating these a little easier. The example in the "SYNOPSIS" section
    could be written as:

      use results;
      use results::exceptions qw( UnexpectedRef UnexpectedUndef );
  
      sub to_uppercase {
        my $str = shift;
     
        return UnexpectedRef->err if ref $str;
        return UnexpectedUndef->err if not defined $str;
    
        return ok( uc $str );
      }

HANDLING RESULTS
  Introduction
    If you call a function which returns a Result, you are *required* to
    handle the result in some way. If a Result goes out of scope or otherwise
    gets destroyed before being handled, this is considered a programming
    error. Currently this will only result in a warning being printed, as Perl
    demotes exceptions thrown in destructors to warnings.

    Results are blessed objects and should be handled by calling methods on
    them.

  Function
   `is_result( $val )`
    Returns true if $val is a Result object. You should rarely need to use
    `is_result()` because a function which returns Results should never return
    anything that isn't a Result.

    `is_result()` is not exported by default, but can be requested:

      use results qw( :default is_result );

  Methods
    The full set of methods available on Results is documented in
    Result::Trait, but a few important ones are described here. These methods
    are `is_err()`, `is_ok()`, `unwrap()`, `unwrap_err()`, `expect()`, and
    `match()`.

   `$result->is_err()`
    Returns true if and only if the Result is an error.

   `$result->is_ok()`
    Returns true if and only if the Result is a success.

   `$result->unwrap()`
    Called on a successful Result, returns the result.

    May be called in scalar or list context, and may return a list if `ok()`
    was given a list.

    If called on an unsuccessful result (error), will promote the error to a
    fatal error. (That is, calls `die`.)

      my $upper_name = to_uppercase( $name );
  
      if ( $upper_name->is_ok() ) {
        say "HELLO ", $upper_name->unwrap();
      }
      else {
        warn "An error occurred!";
      }

    If `unwrap` is called, the Result is considered to be handled.

   `$result->unwrap_err()`
    Called on a unsuccessful Result, returns the error.

    May be called in scalar or list context, and may return a list if `err()`
    was given a list. A list of multiple values rarely makes sense though.

    If called on a successful result (error), will result in a fatal error.
    (That is, calls `die`.)

      my $upper_name = to_uppercase( $name );
  
      if ( $upper_name->is_ok() ) {
        say "HELLO ", $upper_name->unwrap();
      }
      else {
        warn "An error occurred: " . $upper_name->unwrap_err();
      }

    If `unwrap_err` is called, the Result is considered to be handled.

   `$result->expect( $msg )`
    Similar to `unwrap`, but if called on an unsuccessful Result, dies with
    the given error message.

    If `expect` is called, the Result is considered to be handled.

   `$result->match( %dispatch_table )`
    This provides an easy way to deal with different kinds of Results at the
    same time.

      $result->match(
        err_Unauthorized   => sub { ... },
        err_FileNotFound   => sub { ... },
        err                => sub { ... },  # all other errors
        ok                 => sub { ... },
      );

   Other methods
    See Result::Trait for other ways to concisely handle Results.

DIFFERENCES WITH RUST
    Rust is strongly typed and can check many things at compile time which
    this implementation cannot. These must all be done through self-discipline
    in Perl. This includes:

    *   Ensuring that functions which return a Result cannot return a
        non-Result.

    *   Ensuring that the recipient of a Result handles that Result.

    *   Ensuring that the type of the value inside the Result is expected by
        the recipient. (Result::Trait includes a handful of methods for
        run-time enforcement of type constraints though.)

    Methods related to Rust's borrowing, copying, and cloning are not
    implemented in Result::Trait as they do not make a lot of sense.

EXPORTS
    This module exports four functions:

    *   `err`

    *   `ok`

    *   `ok_list`

    *   `is_result`

    By default, only the first two are exported, but you can list the
    functions you want like this:

      use results qw( err ok ok_list is_result );

    Or just:

      use results -all;

    You can import no functions using:

      use results ();

    And then just refer to them by their full name like `results::ok()`.

    You can rename functions:

      use results (
        ok   => { -as => 'Okay' },
        err  => { -as => 'Error' },
      );

    Renaming imports may be useful if you find the default names conflict with
    other modules you're using. In particular, Test::More and other Perl
    testing modules export a function called `ok`.

  Lexical exports
    If you have Perl 5.37.2 or above, or install Lexical::Sub on older
    versions of Perl, you can import this module lexically using:

      use results -lexical;
  
      # or
      use results -lexical, -all;
  
      # or
      use results -lexical, (
        ok   => { -as => 'Okay' },
        err  => { -as => 'Error' },
      );

    results::exceptions also supports lexical exports:

      use results::exceptions -lexical, qw(
        UnexpectedRef
        UnexpectedUndef
      );

BUGS
    Please report any bugs to <https://github.com/tobyink/p5-results/issues>.

SEE ALSO
    Result::Trait, <https://doc.rust-lang.org/std/result/>.

AUTHOR
    Toby Inkster <tobyink@cpan.org>.

COPYRIGHT AND LICENCE
    This software is copyright (c) 2022 by Toby Inkster.

    This is free software; you can redistribute it and/or modify it under the
    same terms as the Perl 5 programming language system itself.

DISCLAIMER OF WARRANTIES
    THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
    WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
    MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.