NAME
FP::Failure - failure values
SYNOPSIS
use
FP::Failure;
is_equal \
@FP::Failure::EXPORT
, [
qw(failure is_failure)
];
# but there is more in EXPORT_OK...
my
$vals
=
do
{
local
$trace_failures
= 0;
list(failure(
"not good"
),
failure(666),
failure(
undef
),
666,
0,
undef
)
};
is_equal
$vals
->
map
(\
&is_failure
),
list(1, 1, 1,
undef
,
undef
,
undef
);
is_equal
$vals
->
map
(
sub
{
my
(
$v
) =
@_
;
$v
?
"t"
:
"f"
}),
list(
"f"
,
"f"
,
"f"
,
"t"
,
"f"
,
"f"
);
# failure dies when called in void context (for safety, failures have
# to be ignored *explicitly*):
is((
eval
{ failure(
"hello"
); 1 } ||
ref
$@),
'FP::Failure::Failure'
);
# get the wrapped value
is_equal
$vals
->filter(\
&is_failure
)->
map
(the_method
"value"
),
list(
"not good"
, 666,
undef
);
# get a nice message
is_equal
$vals
->first->message,
"failure: 'not good'\n"
;
# record backtraces
my
$v
=
do
{
local
$trace_failures
= 1;
failure(666, [
$vals
->first])
};
is_equal
$v
->message,
"failure: 666\n because:\n failure: 'not good'\n"
;
# request recorded backtrace to be shown
use
Path::Tiny;
is_equal regex_substitute(
sub
{
# cleaning up bt
s/line \d+/line .../g;
my
$btlines
= 0;
$_
=
join
(
"\n"
,
grep
{ not /^ \S/ or ++
$btlines
< 2 }
split
/\n/)
},
$v
->message(1)),
join
(
"\n"
,
"failure: 666 at "
.path(
"lib/FP/Failure.pm"
)->canonpath
.
" line ..."
,
" (eval) at lib/FP/Repl/WithRepl.pm line ..."
,
" because:"
,
" failure: 'not good'"
);
# Wrapper that just returns 0 unless configured to create a failure
# object:
use
FP::Show;
is show(
do
{
local
$use_failure
= 0; fails(
"hi"
) }),
0;
is show(
do
{
local
$use_failure
= 1; fails(
"hi"
) }),
"Failure('hi', undef, undef)"
;
# Utility container for holding both a message and values:
is failure(message
"Hi"
,
"foo"
, 9)->message,
"failure: Hi: 'foo', 9\n"
;
is failure(message
"Hi"
)->message,
"failure: Hi\n"
;
# messagefmt is currently still passing everything through FP::Show;
# what should it do, implement another fmt character?
is failure(messagefmt
"Hi %s %d"
,
"foo"
, 9)->message,
"failure: Hi 'foo' 9\n"
;
DESCRIPTION
Values meant to represent errors/failures and to be distinguishable from non-error values. They are overloaded to be false in boolean context (although doing a boolean test is not safe to distinguish from non-failure values, as obviously those include false as well), or checked via the `is_failure` function.
The `value` method delivers the first argument given to `failure`, `maybe_parents` the second, which is an array of the parents, meant for chaining failures (reasons why this failure happened). `message` produces a somewhat nice to read string, multi-line if parents are chained in.
Calling the constructor in void context throws the constructed failure value as an exception.
If the variable `$FP::Failure::trace_failures` is set to true (it can be imported mutably via '*trace_failures'; default: false), then a stack trace is collected with the failures and displayed with `message` (if a true value is passed to message ?). (XX: use `BACKTRACE=1` idea here, too? Implement the same in `Chj::Backtrace`, too, and FP::Repl::Trap if fitting?)
If the variable `$FP::Failure::use_failure` is set to true (it can be imported mutably via '*use_failures'; default: false), then the optionally exported wrapper function `fails` calls `failure` with its arguments, otherwise it returns `0` (fully compatible with standard Perl booleans, and a little bit faster).
TODO
Instead of using `FP::Failure::Failure` as base class, create a failure protocol (FP::Abstract::Failure) instead?
SEE ALSO
FP::Either (which wraps both cases in a shared parent type).
Implements: FP::Abstract::Pure, FP::Struct::Show
NOTE
This is alpha software! Read the status section in the package README or on the website.