NAME
Result::Trait - the trait which all Result objects implement head1 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
The err
, ok
, and ok_list
functions from results return objects which have the methods described in this trait.
Methods
These methods are available on all Result objects.
Many of them will mark the Result as "handled". All Results should be handled.
$result->and( $other_result )
Returns $result
if it is an err. Returns $other_result
otherwise. The effect of this is that and
returns an ok Result only if both Results are ok, and an err Result otherwise.
$result
is considered to be handled if it was ok. $other_result
is not considered to have been handled.
Example
Supposing the create_file()
and upload_file()
functions return Results to indicate success:
my $result = create_file()->and( upload_file() );
if ( $result->is_err() ) {
warn $result->unwrap_err;
}
else {
say "File created and uploaded successfully!";
$result->unwrap();
}
Note that if the create_file()
function failed, the upload_file()
will still be run even though it is destined to fail, because method arguments are evaluated eagerly in Perl. For a solution, see and_then()
.
$result->and_then( sub { THEN } )
The coderef is expected to return a Result object.
Returns $result
if it is an err.
If $result
is ok, then executes the coderef and returns the coderef's Result. Within the coderef, the unwrapped value of $result
in scalar context is available as $_
and in list context is available as @_
.
$result
is considered to be handled if it was ok. $other_result
is not considered to have been handled.
Effectively a version of and
with lazy evaluation of the second operand.
Example
Supposing the create_file()
and upload_file()
functions return Results to indicate success:
my $result = create_file()->and_then( sub { upload_file() } );
if ( $result->is_err() ) {
warn $result->unwrap_err;
}
else {
say "File created and uploaded successfully!";
$result->unwrap();
}
$result->err()
For err Results, the same as unwrap
. For ok Results, returns nothing.
The Result is considered to be handled.
$result->expect( $msg )
For ok Results, unwraps the result.
For err Results, throws an exception with the given message.
The Result is considered to be handled.
$result->expect_err( $msg )
For ok Results, throws an exception with the given message.
For err Results, unwraps the result.
This is the inverse of expect()
.
The Result is considered to be handled.
$result->flatten()
If this is an ok Result containing another Result, returns the inner Result. The outer Result is considered to be handled.
If this is an ok Result not containing another Result, throws.
If this is an err Result, returns self. The Result is not considered handled.
Note this is not a recursive flatten. It only flattens one level of Results.
$result->inspect( sub { PEEK } )
If this is an ok Result, runs the coderef. Within the coderef, the unwrapped value in scalar context is available as $_
and in list context is available as @_
.
If this is an err Result, does nothing.
Always returns self, making it suitable for chaining.
The Result is not considered handled.
$result->inspect_err( sub { PEEK } )
If this is an ok Result, does nothing.
If this is an err Result, runs the coderef. Within the coderef, the unwrapped error in scalar context is available as $_
and in list context is available as @_
.
Always returns self, making it suitable for chaining.
This is the inverse of inspect()
.
The Result is not considered handled.
$result->is_err()
Returns true if and only if this is an err Result.
The Result is not considered handled.
$result->is_ok()
Returns true if and only if this is an ok Result.
The Result is not considered handled.
$result->map( sub { MAP } )
If the Result is ok, then runs the coderef. Within the coderef, the unwrapped value in scalar context is available as $_
and in list context is available as @_
. The return value of the coderef is wrapped in a new ok Result. The original Result is considered to be handled.
If the Result is err, then returns self. The Result is not considered handled.
Example
In the example below, uppercase_name()
will return a Result which may be an ok Result with the uppercased name from the database or the err Result with the message "Could not connect to database".
sub get_name {
if ( connect_to_database() ) {
...;
return ok( $name );
}
else {
return err( "Could not connect to database" );
}
}
sub uppercase_name {
return get_name()->map( sub {
return uc $_;
} );
}
sub lowercase_name {
return get_name()->map( sub {
return lc $_;
} );
}
$result->map_err( sub { MAP } )
If the Result is ok, then returns self. The Result is not considered handled.
If the Result is err, then runs the coderef. Within the coderef, the unwrapped error in scalar context is available as $_
and in list context is available as @_
. The return value of the coderef is wrapped in a new err Result. The original Result is considered to be handled.
This is the inverse of map()
.
$result->map_or( @default, sub { MAP } )
If the Result is ok, then runs the coderef. Within the coderef, the unwrapped value in scalar context is available as $_
and in list context is available as @_
. Returns the return value of the coderef.
If the Result is err, then returns @default (whih may just be a single scalar).
Note that unlike map()
, this does not return a Result, but a value.
The Result is considered to be handled.
Example
In the example below, uppercase_name()
will return a Result which may be an ok Result with the uppercased name from the database or the err Result with the message "Could not connect to database".
sub get_name {
if ( connect_to_database() ) {
...;
return ok( $name );
}
else {
return err( "Could not connect to database" );
}
}
say "HELLO, ", get_name()->map_or( "ANON", sub {
return uc $_;
} );
$result->map_or_else( sub { DEFAULT }, sub { MAP } )
Similar to map_or()
except that the @default
is replaced by a coderef which should return the default.
The Result is considered to be handled.
$result->match( %dispatch_table )
The %dispatch_table
is a hash of coderefs.
The keys 'ok' and 'err' are required coderefs to handle ok and err Results.
(Additional coderefs with keys "err_XXX" are allowed, where "XXX" is a short name for a kind of error. If match
is called on an err Result, and the error is a blessed object which DOES the "results::exceptions" trait, then $result->unwrap_err()->err_kind()
is called and expected to return a string indicating the error kind. The results::exceptions module makes it very easy to create exception objects like this!)
The unwrapped value or error is available in $_
and @_
as you might expect.
This method is not found in the original Rust implementation of Results.
Example
get_name()->match(
ok => sub { say "Hello, $_" },
err => sub { warn $_ },
);
Example
open_file($filename)->match(
ok => sub { $_->write( $data ) },
err_Auth => sub { die( "Permissions error!" ) },
err_DiskFull => sub { die( "Disk is full!" ) },
err => sub { die( "Another error occurred!" ) },
);
$result->ok()
For ok Results, the same as unwrap
. For err Results, returns nothing.
The Result is considered to be handled.
$result->or( $other_result )
Returns $result
if it is ok. Returns $other_result
otherwise. The effect of this is that or
returns an ok Result if either of the Results is ok, and an err Result if both results were err Results.
$result
is considered to be handled if it was an err. $other_result
is not considered to have been handled.
Example
If retrieve_file()
uses a Result to indicate success:
retrieve_file( "server1.example.com" )
->or( retrieve_file( "server2.example.com" ) )
->or( retrieve_file( "server3.example.com" ) )
->expect( "Could not retrieve file from any server!" );
Like with and()
, it needs to be noted that Perl eagerly evaluates method call arguments, so retrieve_file()
will be called three times, even if the first server succeeded. or_else()
provides a solution.
$result->or_else( sub { ELSE } )
The coderef is expected to return a Result object.
Returns $result
if it is ok.
Otherwise, executes the coderef and returns the coderef's Result. Within the coderef, the unwrapped error in scalar context is available as $_
and in list context is available as @_
.
$result
is considered to be handled if it was an err.
Example
If retrieve_file()
uses a Result to indicate success:
retrieve_file( "server1.example.com" )
->or_else( sub {
return retrieve_file( "server2.example.com" );
} )
->or_else( sub {
return retrieve_file( "server3.example.com" );
} )
->expect( "Could not retrieve file from any server!" );
$result->type( $constraint )
If this Result is an err, returns self. Not considered handled.
If this Result is ok, and passes the type constraint in scalar context, returns self. Not considered handled.
Otherwise returns an err Result with the type validation error message. In this case the original Result is considered handled.
This method is not found in the original Rust implementation of Results.
Example
If get_config()
returns an ok Result containing a hashref, then:
use Types::Common qw( HashRef );
my $config = get_config->type( HashRef )->unwrap();
$result->type_or( @default, $constraint )
If this Result is an err, returns self. Not considered handled.
If this Result is ok, and passes the type constraint in scalar context, returns self. Not considered handled.
Otherwise returns an ok Result with the default value(s). In this case the original Result is considered handled.
This method is not found in the original Rust implementation of Results.
Example
If get_config()
returns an ok Result containing a hashref, then:
use Types::Common qw( HashRef );
my $config = get_config->type_or( {}, HashRef )->unwrap();
$result->type_or_else( sub { ELSE }, $constraint )
If this Result is an err, returns self. Not considered handled.
If this Result is ok, and passes the type constraint in scalar context, returns self. Not considered handled.
Otherwise executes the coderef, which is expected to return a Result. In this case the original Result is considered handled.
This method is not found in the original Rust implementation of Results.
$result->unwrap()
For ok Results, returns the value and the Result is considered handled.
For err Results, throws an exception. If you wish to customize the error message, use expect()
instead of unwrap()
.
$result->unwrap_err()
For err Results, returns the error and the Result is considered handled.
For ok Results, throws an exception. If you wish to customize the error message, use expect_err()
instead of unwrap_err()
.
$result->unwrap_or( @default )
For ok Results, returns the value and the Result is considered handled.
For err Results, returns the default value(s).
$result->unwrap_or_else( sub { ELSE } )
For ok Results, returns the value and the Result is considered handled.
For err Results, executes the coderef and returns whatever the coderef returned.
This is effectively a lazy version of unwrap_or()
.
$result->DESTROY()
You should not call this method directly. Called by Perl when the object goes out of scope or is otherwise destroyed.
Attempts to throw an exception if the Result has not been handled. However, the current implementation of Perl downgrades exceptions thrown by DESTROY to be warnings.
Implementing This Trait
This module uses Role::Tiny.
Implementations of this trait need to provide the following methods:
is_err()
,is_ok()
-
As documented above.
_handled()
-
A getter/setter for whether the Result has been handled.
_peek()
-
If the Result is ok, should return the inner value. Should pay attention to list versus scalar context.
Undefined behaviour for err Results.
_peek_err()
-
If the Result is err, should return the inner value. Should pay attention to list versus scalar context.
Undefined behaviour for ok Results.
Implementations may override methods to provide more efficient versions. In particular, unwrap
and unwrap_err
are used a lot internally, but the default implementations are not the fastest.
BUGS
Please report any bugs to https://github.com/tobyink/p5-results/issues.
SEE ALSO
results, https://doc.rust-lang.org/std/result/enum.Result.html.
The unit tests for Result::Trait should provide useful clarifying examples: https://github.com/tobyink/p5-results/blob/master/t/unit/Result/Trait.t.
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.