our
@EXPORT
=
qw(
is_pure
is_pure_object
is_pure_class
is_string
is_path_string
is_path_segment_string
is_nonnumeric_string
is_nonnullstring
is_natural0
is_natural
is_even is_odd
is_boolean01
is_booleanyesno
is_boolean
is_hash
is_array
is_procedure
is_valid_class_name
instance_of
is_instance_of
is_subclass_of
is_filehandle
is_filename
is_sequence
sequence_of
is_proper_sequence
is_seq
less_than
greater_than
less_equal
greater_equal
is_zero
maybe
is_defined
is_true
true
is_false
false
complement
either
all_of both
)
;
our
@EXPORT_OK
=
qw(
is_coderef
$package_re
)
;
our
%EXPORT_TAGS
= (
all
=> [
@EXPORT
,
@EXPORT_OK
]);
sub
failwith {
my
(
$parents
,
$msg
,
@vals
) =
@_
;
$FP::Failure::use_failure
? FP::Failure::failure(FP::Failure::message(
$msg
,
@vals
),
$parents
)
: 0
}
sub
fail {
my
(
$msg
,
@vals
) =
@_
;
$FP::Failure::use_failure
? FP::Failure::failure(FP::Failure::message(
$msg
,
@vals
))
: 0
}
sub
is_pure {
my
(
$v
) =
@_
;
blessed(
$v
) //
return
((
length
ref
$v
) ?
''
: 1);
$v
->isa(
"FP::Abstract::Pure"
) or fail
"is_pure"
,
$v
}
sub
is_pure_object {
my
(
$v
) =
@_
;
blessed(
$v
) //
return
;
$v
->isa(
"FP::Abstract::Pure"
) or fail
"is_pure_object"
,
$v
}
sub
is_pure_class {
my
$r
= is_valid_class_name(
$_
[0]);
$r
or
return
failwith [
$r
],
"is_pure_class"
;
$_
[0]->isa(
"FP::Abstract::Pure"
) or fail
"is_pure_class"
,
$_
[0]
}
sub
is_string {
my
(
$v
) =
@_
;
(
defined
$v
and not
ref
$v
)
or fail
"is_string"
,
$v
}
sub
is_path_string {
my
(
$v
) =
@_
;
(
defined
$v
and not
ref
$v
and
length
$v
and not
$v
=~ /\0/
)
or fail
"is_path_string"
,
$v
}
sub
is_path_segment_string {
my
(
$v
) =
@_
;
(
defined
$v
and not
ref
$v
and
length
$v
and not
$v
=~ m{[\0/]}
and not
$v
eq
".."
and not
$v
eq
"."
)
or fail
"is_path_segment_string"
,
$v
}
sub
is_nonnumeric_string {
my
(
$v
) =
@_
;
(
defined
$v
and not
ref
$v
and not looks_like_number(
$v
)
)
or fail
"is_string"
,
$v
}
sub
is_nonnullstring {
my
(
$v
) =
@_
;
(
defined
$v
and not
ref
$v
and
length
$v
)
or fail
"is_nonnullstring"
,
$v
}
sub
is_natural0 {
my
(
$v
) =
@_
;
(
defined
$v
and not
ref
$v
and
$v
=~ /^\d+\z/
)
or fail
"is_natural0"
,
$v
}
sub
is_natural {
my
(
$v
) =
@_
;
(
defined
$v
and not
ref
$v
and
$v
=~ /^\d+\z/ and
$v
)
or fail
"is_natural"
,
$v
}
sub
is_even {
(
$_
[0] & 1) == 0 or fail
"is_even"
,
$_
[0]
}
sub
is_odd {
(
$_
[0] & 1) or fail
"is_odd"
,
$_
[0]
}
TEST {
[
map
{ is_even
$_
} -3 .. 3]
}
[0, 1, 0, 1, 0, 1, 0];
TEST {
[
map
{ is_odd
$_
} -3 .. 3]
}
[1, 0, 1, 0, 1, 0, 1];
TEST {
[
map
{ is_even
$_
} 3, 3.1, 4, 4.1, -4.1]
}
[0, 0, 1, 1, 1];
sub
less_than {
my
(
$x
) =
@_
;
sub
{
$_
[0] <
$x
or fail
"less_than"
,
$x
,
$_
[0]
}
}
sub
greater_than {
my
(
$x
) =
@_
;
sub
{
$_
[0] >
$x
or fail
"greater_than"
,
$x
,
$_
[0]
}
}
sub
less_equal {
my
(
$x
) =
@_
;
sub
{
$_
[0] <=
$x
or fail
"less_equal"
,
$x
,
$_
[0]
}
}
sub
greater_equal {
my
(
$x
) =
@_
;
sub
{
$_
[0] >=
$x
or fail
"greater_equal"
,
$x
,
$_
[0]
}
}
sub
is_zero {
$_
[0] == 0 or fail
"is_zero"
,
$_
[0]
}
sub
is_boolean01 {
(
not
ref
(
$_
[0])
and
$_
[0] =~ /^[01]\z/
)
or fail
"is_boolean01"
,
$_
[0]
}
sub
is_booleanyesno {
my
(
$v
) =
@_
;
(not
ref
$v
and
$v
eq
"yes"
or
$v
eq
"no"
) or fail
"is_booleanyesno"
,
$v
}
sub
is_boolean {
(
not
ref
(
$_
[0])
and (!
$_
[0] or
$_
[0] eq
"1"
)
)
or fail
"is_boolean"
,
$_
[0]
}
sub
is_hash {
(
defined
$_
[0] and
ref
(
$_
[0]) eq
"HASH"
) or fail
"is_hash"
,
$_
[0]
}
sub
is_array {
(
defined
$_
[0] and
ref
(
$_
[0]) eq
"ARRAY"
) or fail
"is_array"
,
$_
[0]
}
sub
is_coderef {
(
defined
$_
[0] and
ref
(
$_
[0]) eq
"CODE"
) or fail
"is_coderef"
,
$_
[0]
}
sub
is_procedure {
(
defined
$_
[0]
and (
ref
(
$_
[0]) eq
"CODE"
or (
ref
\(
$_
[0]) eq
"GLOB"
? *{
$_
[0] }{CODE} ? 1 :
''
:
''
))
)
or fail
"is_procedure"
,
$_
[0]
}
TEST { is_procedure [] } 0;
TEST { is_procedure \
&is_procedure
} 1;
TEST { is_procedure
*is_procedure
} 1;
TEST { is_procedure
*fifu
} 0;
my
$classpart_re
=
qr/[a-zA-Z_]\w*/
;
our
$package_re
=
qr/(?:${classpart_re}::)*$classpart_re/
;
sub
is_valid_class_name {
my
(
$v
) =
@_
;
!
length
ref
(
$v
) and
$v
=~ /^
$package_re
\z/
or fail
"is_valid_class_name"
,
$v
}
TEST {
[
map
{ is_valid_class_name
$_
}
"foo "
,
qw(foo foo_bar Foo::Bar Foo:Bar Foo123 123 Foo::123)
]
}
[0, 1, 1, 1, 0, 1, 0, 0];
sub
instance_of {
my
(
$class
) =
@_
;
is_valid_class_name
$class
or
die
"need class name string, got: $class"
;
sub
{
((
defined
blessed
$_
[0]) ?
$_
[0]->isa(
$class
) :
''
)
or fail
"instance_of"
,
$class
,
$_
[0]
}
}
sub
is_instance_of {
my
(
$v
,
$class
) =
@_
;
((
defined
blessed
$v
) ?
$v
->isa(
$class
) :
''
)
or fail
"is_instance_of"
,
$v
,
$class
}
sub
is_subclass_of {
my
(
$v
,
$class
) =
@_
;
(!
length
ref
$v
and
$v
->isa(
$class
)) or fail
"is_subclass_of"
,
$v
,
$class
}
TEST {
my
$v
=
"IO"
; is_instance_of
$v
,
"IO"
} 0;
TEST {
my
$v
=
bless
[],
"IO"
; is_instance_of
$v
,
"IO"
} 1;
TEST {
my
$v
=
"IO"
; is_subclass_of
$v
,
"IO"
} 1;
TEST {
is_subclass_of
"Chj::IO::File"
,
"IO"
}
1;
TEST {
[
map
{ is_filehandle
$_
}
"STDOUT"
,
undef
,
*STDOUT
,
*STDOUT
{IO},
\
*STDOUT
,
*SMK69GXDB
,
*SMK69GXDB
{IO},
\
*SMK69GXDB
,
bless
(\
*WOFWEOXVV
,
"ReallyNotIO"
),
do
{
open
my
$in
,
'<'
, __FILE__ or
die
$!;
bless
$in
,
"MightActullyBeIO"
}
]
}
[
''
,
''
,
''
, 1, 1,
''
,
''
,
''
,
''
, 1];
sub
is_filename {
@_
== 1 or fp_croak_arity 1;
my
(
$v
) =
@_
;
(is_nonnullstring(
$v
) and !(
$v
=~ m|/|) and !(
$v
eq
"."
) and !(
$v
eq
".."
))
or fail
"is_filename"
,
$v
}
sub
is_sequence {
@_
== 1 or fp_croak_arity 1;
my
$v
= force
$_
[0];
blessed(
$v
) //
return
;
$v
->isa(
"FP::Abstract::Sequence"
) or fail
"is_sequence"
,
$v
}
sub
is_proper_sequence {
@_
== 1 or fp_croak_arity 1;
my
$v
= force
$_
[0];
blessed(
$v
) //
return
;
(
$v
->isa(
"FP::Abstract::Sequence"
) and
$v
->is_proper_sequence)
or fail
"is_sequence"
,
$v
}
sub
sequence_of {
@_
== 1 or fp_croak_arity 1;
my
(
$pred
) =
@_
;
sub
{
@_
== 1 or fp_croak_arity 1;
my
$v
= force
$_
[0];
blessed(
$v
) //
return
;
(
$v
->isa(
"FP::Abstract::Sequence"
) and
$v
->is_proper_sequence)
or
return
fail
"sequence_of"
,
$v
;
$v
->every(
$pred
)
}
}
sub
is_seq {
@_
== 1 or fp_croak_arity 1;
my
$v
= force
$_
[0];
blessed(
$v
) //
return
;
(
$v
->isa(
"FP::Abstract::Sequence"
) && (not
$v
->is_null))
or fail
"is_sequence"
,
$v
}
sub
maybe {
@_
== 1 or fp_croak_arity 1;
my
(
$pred
) =
@_
;
sub
{
my
(
$v
) =
@_
;
defined
$v
?
do
{
my
$b
=
&$pred
(
$v
);
$b
or failwith [
$b
],
"maybe"
}
: 1
}
}
sub
is_defined {
@_
== 1 or fp_croak_arity 1;
defined
$_
[0] or fail
"is_defined"
,
$_
[0]
}
sub
is_true {
@_
== 1 or fp_croak_arity 1;
$_
[0] or fail
"is_true"
,
$_
[0]
}
sub
is_false {
@_
== 1 or fp_croak_arity 1;
!
$_
[0] or fail
"is_false"
,
$_
[0]
}
sub
true {
1
}
sub
false {
0
}
sub
complement {
@_
== 1 or fp_croak_arity 1;
my
(
$f
) =
@_
;
sub
{
my
$r
=
&$f
(
@_
);
!
$r
or failwith [fail
"not"
],
"complement"
}
}
TEST {
my
$t
= complement(\
&is_natural
);
[
map
{
&$t
(
$_
) } (-1, 0, 1, 2,
"foo"
)]
}
[1, 1, 0, 0, 1];
sub
either {
my
(
@fn
) =
@_
;
sub
{
if
(
$FP::Failure::use_failure
) {
my
@failures
;
for
my
$fn
(
@fn
) {
my
$r
=
&$fn
;
return
$r
if
$r
;
push
@failures
,
$r
}
failwith \
@failures
,
"either"
}
else
{
for
my
$fn
(
@fn
) {
my
$r
=
&$fn
;
return
$r
if
$r
;
}
0
}
}
}
TEST {
my
$t
= either \
&is_natural
, \
&is_boolean
;
[
map
{
&$t
(
$_
) } (-1, 0, 1, 2,
"foo"
)]
}
[0, 1, 1, 2, 0];
sub
all_of {
my
(
@fn
) =
@_
;
sub
{
for
my
$fn
(
@fn
) {
my
$r
=
&$fn
;
return
failwith [
$r
],
"all_of"
unless
$r
;
}
1
}
}
sub
both {
@_
== 2 or fp_croak_arity 2;
all_of(
@_
)
}
1