NAME
Error::ROP - A simple and lightweight implementation error handling library for Perl, inspired in the Rop type.
SYNOPSIS
use Error::ROP qw(rop);
my $meaning = rop { 80 / $divisor }->then(sub { $_ + 2 });
say "The life meaning is " . $meaning->value if $meaning->is_valid;
warn "Life has no meaning" if not $meaning->is_valid;
DESCRIPTION
The purpose of the rop
function is to let you focus in the happy path and provide a nice way to treat failures without filling the code with eval
s and if
s that always serve almost the same purpose.
Supose you have a computation that can fail depending on some condition. For the sake of simplicity consider the following code
sub compute_meaning {
my $divisor = shift;
return 2 + 80 / $divisor;
};
that will fail when called with a zero argument.
Following the style of the Railway Oriented Programming, you wrap the part that could fail in a rop
block and focus on programming the happy path:
sub compute_meaning {
my $divisor = shift;
return rop { 80 / $divisor }
->then(sub { $_ + 2 });
};
This way, the compute_meaning
function will never blow, even when passed in a zero argument and the computation doesn't make sense. The caller can check that the computation succeeded by asking the rop
result object.
When the computation succeeds, the value
property contains the computation result
my $meaning = compute_meaning(2);
say "The life meaning is " $meaning->value if $meaning->is_valid;
and when the computation fails, you can also inform the user or decide how to proceed, by inspecting the failure
value, which will contain the captured error.
my $meaning = compute_meaning(0);
warn "Life has no meaning: " . $meaning->failure if not $meaning->is_valid;
Chaining
The real usability gain of using rop
occurs when you have a recipe that comprises several things to do and you need to stop at the first step that fails.
That is, you need to chain or compose several functions that in the happy path would be executed one after another but in the real path, you would have to check for any of them if had failed or not and proceed with the next or stop and report the errors.
With rop
you can leverage the checking to the library and just program the happy path functions and chain them with the then
method:
use Error::ROP;
my $res = rop { 40 / $something }
->then(sub { $_ / 2 })
->then(sub { $_ * 4 })
->then(sub { $_ + 2 });
You can always know if the computation has succed by inspecting the rop,
say $res->value if $res->is_valid;
or treat the error otherwise
warn $res->failure if not $res->is_valid;
The computation will short-circuit and return with the first error occurred, no matter how many chained functions remain after the failing step.
On Either types and then
This module does not implement the Either type in Perl. The Haskell, F#, ML and other strongly typed functional programming languages have Either types. This is not a generic type like Haskell's Either a b
.
On those PL you have a strong type system and generic programming facilities that allow you to generalize operations into higher abstractions. In particular, you can operate in elevated (monadic) types as if they where first class values and the languages provide tools (generic functions and operators) that allow you to compose those operations by somehow overloading composition.
When adopting an Either type to implement ROP in those languages, you normally use the >=>
operator to overload composition. Actually, you use it to compose functions of the type
>=> :: (a -> Either b e) -> (b -> Either c e) -> (a -> Either c e)
This library just uses a wrapper object (the Error::ROP instance) that has a method then
to somehow compose other operations. This is a much less flexible approach but it works and is easy to understand. The two leaves of the type are accessible via the instance's value
and failure
getters.
The only confusion might be that it ressembles the then
function of a promise or future. This is not exactly the same. Just keep that in mind.
USAGE
You can find more usage examples in the tests t/Then.t
. For examples of how to use inside Moose t/Example.t
Running tests
A Dockerfile
is provided in order to run the tests without needing any perl in your system. Just run:
$ make -f Makefile.docker test
This should construct an image with the necessary dependencies, copy the source into the image and run the tests.
AUTHOR
CAPSiDE
BUGS and SOURCE
The source code is located here: https://github.com/paudirac/Error-ROP
Please report bugs to: https://github.com/paudirac/Error-ROP/issues
COPYRIGHT and LICENSE
Copyright (c) 2017 by CAPSiDE
This code is distributed under the Apache 2 License. The full text of the license can be found in the LICENSE file included with this module.