| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # ABSTRACT: Error-ROP - A simple and lightweight implementation error handling library for Perl, | 
| 2 |  |  |  |  |  |  | # inspired in the Either type. | 
| 3 |  |  |  |  |  |  | package Error::ROP; | 
| 4 | 5 |  |  | 5 |  | 563175 | use strict; | 
|  | 5 |  |  |  |  | 12 |  | 
|  | 5 |  |  |  |  | 146 |  | 
| 5 | 5 |  |  | 5 |  | 26 | use warnings; | 
|  | 5 |  |  |  |  | 11 |  | 
|  | 5 |  |  |  |  | 128 |  | 
| 6 | 5 |  |  | 5 |  | 1178 | use Error::ROP::Imp; | 
|  | 5 |  |  |  |  | 62 |  | 
|  | 5 |  |  |  |  | 462 |  | 
| 7 | 5 |  |  | 5 |  | 67 | use Exporter qw/import/; | 
|  | 5 |  |  |  |  | 15 |  | 
|  | 5 |  |  |  |  | 1607 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | our @EXPORT_OK = qw/success failure rop bind/; | 
| 10 |  |  |  |  |  |  | our $VERSION = '0.02'; | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | sub success { | 
| 13 | 19 |  |  | 19 | 0 | 3724 | return Error::ROP::Imp->new(value => shift); | 
| 14 |  |  |  |  |  |  | } | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | sub failure { | 
| 17 | 9 |  |  | 9 | 0 | 1382 | return Error::ROP::Imp->new(failure => shift); | 
| 18 |  |  |  |  |  |  | } | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | sub rop (&) { | 
| 21 | 20 |  |  | 20 | 0 | 23829 | my $code = shift; | 
| 22 |  |  |  |  |  |  |  | 
| 23 | 20 |  |  |  |  | 29 | my $res = eval { | 
| 24 | 20 |  |  |  |  | 40 | $code->(@_); | 
| 25 |  |  |  |  |  |  | }; | 
| 26 | 20 | 100 |  |  |  | 187 | return failure($@) if $@; | 
| 27 | 14 |  |  |  |  | 29 | return success($res); | 
| 28 |  |  |  |  |  |  | } | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | # Either a -> (a -> Either b) -> Either b | 
| 31 |  |  |  |  |  |  | sub bind { | 
| 32 | 2 |  |  | 2 | 0 | 11 | my $either = shift @_; | 
| 33 | 2 |  |  |  |  | 3 | my $fn = \&{shift @_}; | 
|  | 2 |  |  |  |  | 4 |  | 
| 34 | 2 | 100 |  | 1 |  | 7 | return $either->is_valid ? rop { $fn->($either->value) } : $either; | 
|  | 1 |  |  |  |  | 21 |  | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | 1; | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | =encoding utf8 | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | =head1 NAME | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | Error::ROP - A simple and lightweight implementation error handling library for Perl, | 
| 44 |  |  |  |  |  |  | inspired in the Rop type. | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | use Error::ROP qw(rop); | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | my $meaning =  rop { 80 / $divisor }->then(sub { $_ + 2 }); | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | say "The life meaning is " . $meaning->value if $meaning->is_valid; | 
| 53 |  |  |  |  |  |  | warn "Life has no meaning" if not $meaning->is_valid; | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | The purpose of the C<< rop >> function is to let you focus in the happy path | 
| 58 |  |  |  |  |  |  | and provide a nice way to treat failures without filling the code | 
| 59 |  |  |  |  |  |  | with C<< eval >>s and C<< if >>s that always serve almost the same purpose. | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | Supose you have a computation that can fail depending on some condition. | 
| 62 |  |  |  |  |  |  | For the sake of simplicity consider the following code | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | sub compute_meaning { | 
| 65 |  |  |  |  |  |  | my $divisor = shift; | 
| 66 |  |  |  |  |  |  | return 2 + 80 / $divisor; | 
| 67 |  |  |  |  |  |  | }; | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | that will fail when called with a zero argument. | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | Following the style of the L<Railway Oriented Programming|https://fsharpforfunandprofit.com/rop/>, you wrap the part | 
| 72 |  |  |  |  |  |  | that could fail in a C<< rop >> block and focus on programming the happy | 
| 73 |  |  |  |  |  |  | path: | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | sub compute_meaning { | 
| 76 |  |  |  |  |  |  | my $divisor = shift; | 
| 77 |  |  |  |  |  |  | return rop { 80 / $divisor } | 
| 78 |  |  |  |  |  |  | ->then(sub { $_ + 2 }); | 
| 79 |  |  |  |  |  |  | }; | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | This way, the C<< compute_meaning >> function will never blow, even when | 
| 82 |  |  |  |  |  |  | passed in a zero argument and the computation doesn't make sense. The caller | 
| 83 |  |  |  |  |  |  | can check that the computation succeeded by asking the C<< rop >> result | 
| 84 |  |  |  |  |  |  | object. | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | When the computation succeeds, the C<< value >> property contains | 
| 87 |  |  |  |  |  |  | the computation result | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | my $meaning = compute_meaning(2); | 
| 90 |  |  |  |  |  |  | say "The life meaning is " $meaning->value if $meaning->is_valid; | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | and when the computation fails, you can also inform the user or decide how to | 
| 93 |  |  |  |  |  |  | proceed, by inspecting the C<< failure >> value, which will contain the captured | 
| 94 |  |  |  |  |  |  | error. | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | my $meaning = compute_meaning(0); | 
| 97 |  |  |  |  |  |  | warn "Life has no meaning: " . $meaning->failure if not $meaning->is_valid; | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | =head2 Chaining | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | The real usability gain of using C<< rop >> occurs when you have a recipe | 
| 103 |  |  |  |  |  |  | that comprises several things to do and you need to stop at the first step | 
| 104 |  |  |  |  |  |  | that fails. | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | That is, you need to chain or compose several functions that | 
| 107 |  |  |  |  |  |  | in the happy path would be executed one after another but in the real path, you | 
| 108 |  |  |  |  |  |  | would have to check for any of them if had failed or not and proceed with | 
| 109 |  |  |  |  |  |  | the next or stop and report the errors. | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | With C<< rop >> you can leverage the checking to the library and just program | 
| 112 |  |  |  |  |  |  | the happy path functions and chain them with the C<< then >> method: | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | use Error::ROP; | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | my $res = rop { 40 / $something } | 
| 117 |  |  |  |  |  |  | ->then(sub { $_ / 2 }) | 
| 118 |  |  |  |  |  |  | ->then(sub { $_ * 4 }) | 
| 119 |  |  |  |  |  |  | ->then(sub { $_ + 2 }); | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | You can always know if the computation has succed by inspecting the rop, | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | say $res->value if $res->is_valid; | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | or treat the error otherwise | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | warn $res->failure if not $res->is_valid; | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | The computation will short-circuit and return with the first error occurred, | 
| 130 |  |  |  |  |  |  | no matter how many chained functions remain after the failing step. | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | =head2 On Either types and then | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | This module does not implement the Either type in Perl. The Haskell, F#, ML and | 
| 135 |  |  |  |  |  |  | other strongly typed functional programming languages have Either types. This | 
| 136 |  |  |  |  |  |  | is not a generic type like Haskell's C<Either a b>. | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | On those PL you have a strong type system and generic programming facilities that | 
| 139 |  |  |  |  |  |  | allow you to generalize operations into higher abstractions. In particular, you | 
| 140 |  |  |  |  |  |  | can operate in elevated (monadic) types as if they where first class values and the | 
| 141 |  |  |  |  |  |  | languages provide tools (generic functions and operators) that allow you to | 
| 142 |  |  |  |  |  |  | compose those operations by somehow overloading composition. | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | When adopting an Either type to implement ROP in those languages, you normally use | 
| 145 |  |  |  |  |  |  | the C< E<gt>=E<gt> > operator to overload composition. Actually, you use it to compose | 
| 146 |  |  |  |  |  |  | functions of the type | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | >=> :: (a -> Either b e) -> (b -> Either c e) -> (a -> Either c e) | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | This library just uses a wrapper object (the Error::ROP instance) that has a method C<then> to somehow | 
| 151 |  |  |  |  |  |  | compose other operations. This is a much less flexible approach but it works and is easy to understand. | 
| 152 |  |  |  |  |  |  | The two leaves of the type are accessible via the instance's C<value> and C<failure> getters. | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | The only confusion might be that it ressembles the C<then> function of a promise or future. This is not | 
| 155 |  |  |  |  |  |  | exactly the same. Just keep that in mind. | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | =head2 USAGE | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | You can find more usage examples in the tests C<t/Then.t>. For examples of | 
| 160 |  |  |  |  |  |  | how to use inside Moose C<t/Example.t> | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | =head2 Running tests | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | A C<Dockerfile> is provided in order to run the tests without needing | 
| 165 |  |  |  |  |  |  | any perl in your system. Just run: | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | $ make -f Makefile.docker test | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | This should construct an image with the necessary dependencies, copy | 
| 170 |  |  |  |  |  |  | the source into the image and run the tests. | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | =head1 AUTHOR | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | L<Pau Cervera i Badia|pau.cervera@capside.com> | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | CAPSiDE | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | =head1 BUGS and SOURCE | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | The source code is located here: L<https://github.com/paudirac/Error-ROP> | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | Please report bugs to: L<https://github.com/paudirac/Error-ROP/issues> | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | =head1 COPYRIGHT and LICENSE | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | Copyright (c) 2017 by CAPSiDE | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | 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. | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | =cut |