use
lib
qw( ./lib ./t/lib ../inc ./inc )
;
ok(
! Dict->of(
x
=> Int)->has_coercion,
"Dict of type without coercion shouldn't have coercion"
,
);
ok(
Dict->of(
x
=> Int->plus_coercions(Any, 1))->has_coercion,
"Dict of type with coercion should have coercion"
,
);
ok(
! Tuple->of(Int)->has_coercion,
"Tuple of type without coercion shouldn't have coercion"
,
);
ok(
Tuple->of(Int->plus_coercions(Any, 1))->has_coercion,
"Tuple of type with coercion should have coercion"
,
);
ok(
! Map->of(Str, Int)->has_coercion,
"Map of type without coercion shouldn't have coercion"
,
);
ok(
Map->of(Str, Int->plus_coercions(Any, 1))->has_coercion,
"Map of type with coercion should have coercion"
,
);
NONINLINED: {
my
$Foo
= declare
Foo
=> as Int;
coerce
$Foo
, from Num, via {
int
(
$_
) };
my
$ArrayOfFoo
= declare
ArrayOfFoo
=> as ArrayRef[
$Foo
],
coercion
=> 1;
ok(
$ArrayOfFoo
->has_coercion,
'$ArrayOfFoo has coercion'
);
my
$arr1
= [1..3];
my
$arr2
= [1..3,
"Hello"
];
is(
$ArrayOfFoo
->coerce(
$arr1
),
$arr1
,
'$ArrayOfFoo does not coerce value that needs no coercion'
,
);
is_deeply(
$ArrayOfFoo
->coerce([1.1, 2.1, 3.1]),
[1, 2, 3],
'$ArrayOfFoo does coerce value that can be coerced'
,
);
is(
$ArrayOfFoo
->coerce(
$arr2
),
$arr2
,
'$ArrayOfFoo does not coerce value that cannot be coerced'
,
);
my
$HashOfFoo
= HashRef[
$Foo
];
ok(
$HashOfFoo
->has_coercion,
'$HashOfFoo has coercion'
);
my
$hsh1
= {
one
=> 1,
two
=> 2,
three
=> 3};
my
$hsh2
= {
one
=> 1,
two
=> 2,
three
=> 3,
greeting
=>
"Hello"
};
is(
$HashOfFoo
->coerce(
$hsh1
),
$hsh1
,
'$HashOfFoo does not coerce value that needs no coercion'
,
);
is_deeply(
$HashOfFoo
->coerce({
one
=> 1.1,
two
=> 2.2,
three
=> 3.3}),
{
one
=> 1,
two
=> 2,
three
=> 3},
'$HashOfFoo does coerce value that can be coerced'
,
);
is(
$HashOfFoo
->coerce(
$hsh2
),
$hsh2
,
'$HashOfFoo does not coerce value that cannot be coerced'
,
);
my
$RefOfFoo
= ScalarRef[
$Foo
];
ok(
$RefOfFoo
->has_coercion,
'$RefOfFoo has coercion'
);
my
$ref1
=
do
{
my
$x
= 1; \
$x
};
my
$ref2
=
do
{
my
$x
=
"xxx"
; \
$x
};
is(
$RefOfFoo
->coerce(
$ref1
),
$ref1
,
'$RefOfFoo does not coerce value that needs no coercion'
,
);
is_deeply(
${
$RefOfFoo
->coerce(
do
{
my
$x
= 1.1; \
$x
}) },
1,
'$RefOfFoo does coerce value that can be coerced'
,
);
is(
$RefOfFoo
->coerce(
$ref2
),
$ref2
,
'$RefOfFoo does not coerce value that cannot be coerced'
,
);
my
$MaybeFoo
= Maybe[
$Foo
->plus_coercions(Undef, 999)];
is(
$MaybeFoo
->coerce(
undef
),
undef
,
'$MaybeFoo does not coerce undef'
,
);
is(
$MaybeFoo
->coerce(42),
42,
'$MaybeFoo does not coerce integer'
,
);
is(
$MaybeFoo
->coerce(4.2),
4,
'$MaybeFoo does coerce non-integer number'
,
);
is(
$MaybeFoo
->coerce(
"xyz"
),
"xyz"
,
'$MaybeFoo cannot coerce non-number'
,
);
};
INLINED: {
my
$Bar
= declare
Bar
=> as Int;
coerce
$Bar
, from Num,
q {
int
(
$_
) };
$Bar
->coercion->freeze;
my
$ArrayOfBar
= ArrayRef[
$Bar
];
$ArrayOfBar
->coercion->freeze;
ok(
$ArrayOfBar
->has_coercion,
'$ArrayOfBar has coercion'
);
ok(
$ArrayOfBar
->coercion->can_be_inlined,
'$ArrayOfBar coercion can be inlined'
);
my
$arr1
= [1..3];
my
$arr2
= [1..3,
"Hello"
];
is(
$ArrayOfBar
->coerce(
$arr1
),
$arr1
,
'$ArrayOfBar does not coerce value that needs no coercion'
,
);
is_deeply(
$ArrayOfBar
->coerce([1.1, 2.1, 3.1]),
[1, 2, 3],
'$ArrayOfBar does coerce value that can be coerced'
,
);
is(
$ArrayOfBar
->coerce(
$arr2
),
$arr2
,
'$ArrayOfBar does not coerce value that cannot be coerced'
,
);
my
$HashOfBar
= HashRef[
$Bar
];
$HashOfBar
->coercion->freeze;
ok(
$HashOfBar
->has_coercion,
'$HashOfBar has coercion'
);
ok(
$HashOfBar
->coercion->can_be_inlined,
'$HashOfBar coercion can be inlined'
);
my
$hsh1
= {
one
=> 1,
two
=> 2,
three
=> 3};
my
$hsh2
= {
one
=> 1,
two
=> 2,
three
=> 3,
greeting
=>
"Hello"
};
is(
$HashOfBar
->coerce(
$hsh1
),
$hsh1
,
'$HashOfBar does not coerce value that needs no coercion'
,
);
is_deeply(
$HashOfBar
->coerce({
one
=> 1.1,
two
=> 2.2,
three
=> 3.3}),
{
one
=> 1,
two
=> 2,
three
=> 3},
'$HashOfBar does coerce value that can be coerced'
,
);
is(
$HashOfBar
->coerce(
$hsh2
),
$hsh2
,
'$HashOfBar does not coerce value that cannot be coerced'
,
);
my
$RefOfBar
= ScalarRef[
$Bar
];
$RefOfBar
->coercion->freeze;
ok(
$RefOfBar
->has_coercion,
'$RefOfBar has coercion'
);
ok(
$RefOfBar
->coercion->can_be_inlined,
'$RefOfBar coercion can be inlined'
);
my
$ref1
=
do
{
my
$x
= 1; \
$x
};
my
$ref2
=
do
{
my
$x
=
"xxx"
; \
$x
};
is(
$RefOfBar
->coerce(
$ref1
),
$ref1
,
'$RefOfBar does not coerce value that needs no coercion'
,
);
is_deeply(
${
$RefOfBar
->coerce(
do
{
my
$x
= 1.1; \
$x
}) },
1,
'$RefOfBar does coerce value that can be coerced'
,
);
is(
$RefOfBar
->coerce(
$ref2
),
$ref2
,
'$RefOfBar does not coerce value that cannot be coerced'
,
);
my
$MaybeBar
= Maybe[
$Bar
->plus_coercions(Undef, 999)];
$MaybeBar
->coercion->freeze;
is(
$MaybeBar
->coerce(
undef
),
undef
,
'$MaybeBar does not coerce undef'
,
);
is(
$MaybeBar
->coerce(42),
42,
'$MaybeBar does not coerce integer'
,
);
is(
$MaybeBar
->coerce(4.2),
4,
'$MaybeBar does coerce non-integer number'
,
);
is(
$MaybeBar
->coerce(
"xyz"
),
"xyz"
,
'$MaybeBar cannot coerce non-number'
,
);
};
MAP: {
my
$IntFromStr
= declare
IntFromStr
=> as Int;
coerce
$IntFromStr
, from Str,
q{ length($_) }
;
my
$IntFromNum
= declare
IntFromNum
=> as Int;
coerce
$IntFromNum
, from Num,
q{ int($_) }
;
my
$IntFromArray
= declare
IntFromArray
=> as Int;
coerce
$IntFromArray
, from ArrayRef, via {
scalar
(
@$_
) };
$_
->coercion->freeze
for
$IntFromStr
,
$IntFromNum
,
$IntFromArray
;
my
$Map1
= Map[
$IntFromNum
,
$IntFromStr
];
ok(
$Map1
->has_coercion &&
$Map1
->coercion->can_be_inlined,
"$Map1 has an inlinable coercion"
,
);
is_deeply(
$Map1
->coerce({ 1.
1
=>
"Hello"
, 2.
1
=>
"World"
, 3.
1
=>
"Hiya"
}),
{
1
=> 5,
2
=> 5,
3
=> 4 },
"Coercions to $Map1"
,
);
is_deeply(
$Map1
->coerce({ 1.
1
=>
"Hello"
, 2.
1
=>
"World"
, 3.
1
=> [] }),
{ 1.
1
=>
"Hello"
, 2.
1
=>
"World"
, 3.
1
=> [] },
"Impossible coercion to $Map1"
,
);
my
$m
= {
1
=> 2 };
is(
$Map1
->coerce(
$m
),
$m
,
"Unneeded coercion to $Map1"
,
);
my
$Map2
= Map[
$IntFromNum
,
$IntFromArray
];
ok(
$Map2
->has_coercion && !
$Map2
->coercion->can_be_inlined,
"$Map2 has a coercion, but it cannot be inlined"
,
);
is_deeply(
$Map2
->coerce({ 1.
1
=> [1], 2.
1
=> [1,2], 3.
1
=> [] }),
{
1
=> 1,
2
=> 2,
3
=> 0 },
"Coercions to $Map2"
,
);
is_deeply(
$Map2
->coerce({ 1.
1
=> [1], 2.
1
=> [1,2], 3.
1
=> {} }),
{ 1.
1
=> [1], 2.
1
=> [1,2], 3.
1
=> {} },
"Impossible coercion to $Map2"
,
);
$m
= {
1
=> 2 };
is(
$Map2
->coerce(
$m
),
$m
,
"Unneeded coercion to $Map2"
,
);
};
DICT: {
my
$IntFromStr
= declare
IntFromStr
=> as Int;
coerce
$IntFromStr
, from Str,
q{ length($_) }
;
my
$IntFromNum
= declare
IntFromNum
=> as Int;
coerce
$IntFromNum
, from Num,
q{ int($_) }
;
my
$IntFromArray
= declare
IntFromArray
=> as Int;
coerce
$IntFromArray
, from ArrayRef, via {
scalar
(
@$_
) };
$_
->coercion->freeze
for
$IntFromStr
,
$IntFromNum
,
$IntFromArray
;
my
@a
= (
a
=>
$IntFromStr
,
b
=>
$IntFromNum
,
c
=> Optional[
$IntFromNum
]);
my
$Dict1
= Dict[
a
=>
$IntFromStr
,
b
=>
$IntFromNum
,
c
=> Optional[
$IntFromNum
] ];
ok(
$Dict1
->has_coercion &&
$Dict1
->coercion->can_be_inlined,
"$Dict1 has an inlinable coercion"
,
);
is_deeply(
$Dict1
->coerce({
a
=>
"Hello"
,
b
=> 1.1,
c
=> 2.2 }),
{
a
=> 5,
b
=> 1,
c
=> 2 },
"Coercion (A) to $Dict1"
,
);
is_deeply(
$Dict1
->coerce({
a
=>
"Hello"
,
b
=> 1 }),
{
a
=> 5,
b
=> 1 },
"Coercion (B) to $Dict1"
,
);
is_deeply(
$Dict1
->coerce({
a
=>
"Hello"
,
b
=> 1,
c
=> [],
d
=> 1 }),
{
a
=>
"Hello"
,
b
=> 1,
c
=> [],
d
=> 1 },
"Coercion (C) to $Dict1 - changed in 0.003_11; the presence of an additional value cancels coercion"
,
);
};
DICT_PLUS_SLURPY: {
my
$Rounded1
= Int->plus_coercions(Num,
q[int($_)]
);
my
$Dict1
= Dict[
a
=>
$Rounded1
, slurpy Map[
$Rounded1
,
$Rounded1
] ];
is_deeply(
$Dict1
->coerce({
a
=> 1.1, 2.
2
=> 3.3, 4.
4
=> 5 }),
{
a
=> 1,
2
=> 3,
4
=> 5 },
"Coercion to $Dict1 (inlined)"
,
);
my
$Rounded2
= Int->plus_coercions(Num,
sub
{
int
(
$_
) });
my
$Dict2
= Dict[
a
=>
$Rounded2
, slurpy Map[
$Rounded2
,
$Rounded2
] ];
is_deeply(
$Dict2
->coerce({
a
=> 1.1, 2.
2
=> 3.3, 4.
4
=> 5 }),
{
a
=> 1,
2
=> 3,
4
=> 5 },
"Coercion to $Dict2 (non-inlined)"
,
);
};
DICT_PLUS_OPTIONAL: {
my
$IntFromStr
= declare
IntFromStr
=> as Int;
coerce
$IntFromStr
, from Str,
sub
{
length
(
$_
) };
$IntFromStr
->coercion->freeze;
my
$Dict1
= Dict[
a
=>
$IntFromStr
,
b
=> Optional[Int],
c
=> Optional[Int] ];
ok(
$Dict1
->has_coercion && !
$Dict1
->coercion->can_be_inlined,
"$Dict1 has a non-inlinable coercion"
,
);
is_deeply(
$Dict1
->coerce({
a
=>
"Hello"
,
b
=> 1,
c
=> 2 }),
{
a
=> 5,
b
=> 1,
c
=> 2 },
"Coercion (A) to $Dict1"
,
);
is_deeply(
$Dict1
->coerce({
a
=>
"Hello"
,
b
=> 1 }),
{
a
=> 5,
b
=> 1 },
"Coercion (B) to $Dict1"
,
);
};
TUPLE: {
my
$IntFromStr
= declare
IntFromStr
=> as Int;
coerce
$IntFromStr
, from Str,
q{ length($_) }
;
my
$IntFromNum
= declare
IntFromNum
=> as Int;
coerce
$IntFromNum
, from Num,
q{ int($_) }
;
my
$IntFromArray
= declare
IntFromArray
=> as Int;
coerce
$IntFromArray
, from ArrayRef, via {
scalar
(
@$_
) };
$_
->coercion->freeze
for
$IntFromStr
,
$IntFromNum
,
$IntFromArray
;
my
$Tuple1
= Tuple[
$IntFromNum
, Optional[
$IntFromStr
], slurpy ArrayRef[
$IntFromNum
]];
ok(
$Tuple1
->has_coercion &&
$Tuple1
->coercion->can_be_inlined,
"$Tuple1 has an inlinable coercion"
,
);
is_deeply(
$Tuple1
->coerce([
qw( 1.1 1.1 )
]),
[1, 3],
"Coercion (A) to $Tuple1"
,
);
is_deeply(
$Tuple1
->coerce([
qw( 1.1 1.1 2.2 2.2 33 3.3 )
]),
[1, 3, 2, 2, 33, 3],
"Coercion (B) to $Tuple1"
,
);
my
$Tuple2
= Tuple[
$IntFromNum
];
is_deeply(
$Tuple2
->coerce([
qw( 1.1 )
]),
[ 1 ],
"Coercion (A) to $Tuple2"
,
);
is_deeply(
$Tuple2
->coerce([
qw( 1.1 2.2 )
]),
[ 1.1, 2.2 ],
"Coercion (B) to $Tuple2 - changed in 0.003_11; the presence of an additional value cancels coercion"
,
);
my
$EvenInt
= Int->create_child_type(
name
=>
'EvenInt'
,
constraint
=>
sub
{ not
$_
% 2 },
);
my
$Tuple3
= Tuple[
$EvenInt
->plus_coercions(Int,
sub
{ 2 *
$_
}) ];
ok(
$Tuple3
->check([4]) );
ok( not
$Tuple3
->check([3]) );
is_deeply(
$Tuple3
->coerce([4]),
[4],
"No coercion necessary to $Tuple3"
,
);
is_deeply(
$Tuple3
->coerce([3]),
[6],
"Coercion to $Tuple3"
,
);
my
$EvenInt2
= Int->create_child_type(
name
=>
'EvenInt2'
,
constraint
=>
sub
{ not
$_
% 2 },
inlined
=>
sub
{
undef
,
"not($_ % 2)"
},
);
my
$Tuple4
= Tuple[
$EvenInt2
->plus_coercions(Int,
q{ 2 * $_ }
) ];
ok(
$Tuple4
->check([4]) );
ok( not
$Tuple4
->check([3]) );
is_deeply(
$Tuple4
->coerce([4]),
[4],
"No coercion necessary to $Tuple4"
,
);
is_deeply(
$Tuple4
->coerce([3]),
[6],
"Coercion to $Tuple4"
,
);
};
TUPLE: {
my
$IntFromStr
= declare
IntFromStr
=> as Int;
coerce
$IntFromStr
, from Str,
q{ length($_) }
;
is_deeply(
Tuple->of(HashRef, slurpy ArrayRef[
$IntFromStr
])->coerce([{}, 1, 2.2,
"Hello"
,
"world"
]),
[{}, 1, 3, 5, 5],
'coercing Tuple with slurpy arrayref'
);
};
THINGY1: {
my
$IntFromStr
= declare
IntFromStr
=> as Int;
coerce
$IntFromStr
, from Str,
q{ length($_) }
;
is_deeply(
Tuple->of(
$IntFromStr
)->coerce([
"Hello"
,
"world"
]),
[
"Hello"
,
"world"
],
'inlinable coercion of Tuple with no slurpy given input with extra fields fails'
);
};
THINGY2: {
my
$IntFromStr
= declare
IntFromStr
=> as Int;
coerce
$IntFromStr
, from Str,
sub
{
length
(
$_
) };
is_deeply(
Tuple->of(
$IntFromStr
)->coerce([
"Hello"
,
"world"
]),
[
"Hello"
,
"world"
],
'non-inlinable coercion of Tuple with no slurpy given input with extra fields fails'
);
};
THINGY3: {
my
$IntFromStr
= Int->plus_coercions( Str,
'length($_)'
);
my
$Tuple
= Dict->of(
xyz
=>
$IntFromStr
, slurpy HashRef[Int] );
is_deeply(
$Tuple
->coerce( {
xyz
=>
"Foo"
,
abc
=> 4 } ),
{
xyz
=> 3,
abc
=> 4 },
'Dict where key has inlineable coercion but slurpy has no coercion'
);
is_deeply(
$Tuple
->coerce( {
xyz
=>
"Foo"
,
abc
=> 4.1 } ),
{
xyz
=>
"Foo"
,
abc
=> 4.1 },
'... all or nothing'
);
}
THINGY4: {
my
$IntFromStr
= Int->plus_coercions( Str,
sub
{
length
(
$_
) } );
my
$Tuple
= Dict->of(
xyz
=>
$IntFromStr
, slurpy HashRef[Int] );
is_deeply(
$Tuple
->coerce( {
xyz
=>
"Foo"
,
abc
=> 4 } ),
{
xyz
=> 3,
abc
=> 4 },
'Dict where key has non-inlineable coercion but slurpy has no coercion'
);
is_deeply(
$Tuple
->coerce( {
xyz
=>
"Foo"
,
abc
=> 4.1 } ),
{
xyz
=>
"Foo"
,
abc
=> 4.1 },
'... all or nothing'
);
}
done_testing;