Reputation: 132896
In Perl 6 you can specify a type that a type can be coerced to. For example, you need an Int
but get something else that can convert to an Int
. This is handy when you don't want separate candidates for Int
and Str
where the string represents an integer value.
But, it seems that the conversion is a bit aggressive because the conversion not only changes the type but is willing to change the data. It's partly a problem of the conflation of changing types and an expected operation to truncate a number to an integer. Conceptually those are different ideas but they are intertwined in Str.Int
(actually sandwiching a side trip to Numeric
):
sub foo ( Int:D() $n ) { put "Got <$n> of type {$n.^name}" }
foo( 80 ); # Got <80> of type Int
foo( '99' ); # Got <99> of type Int
foo( 1.5 ); # Got <1> of type Int
foo( '1.5' ); # Got <1> of type Int
Trying to limit this to Str
isn't any better:
sub foo ( Int:D(Str:D) $n ) { put "Got <$n> of type {$n.^name}" }
foo( '1.5' ); # Got <1> of type Int
I could make some adapters which seems the easiest to understand:
multi foo ( Int:D $n ) {
put "Got <$n> of type {$n.^name}"
}
multi foo ( Str:D $n where { $^n.Int == $^n.Numeric } ) {
foo( $n.Int );
}
foo( '1.5' ); # Cannot resolve ...
And I can probably come up with some subsets but that's not any more satisfying. So the trick is, can I coerce like this without changing the value (even if it changes representation)?
It turns out that this feature is broken and doesn't have a timeline for repair: RT 132980. Basically, the target type is not enforced. The docs are updated. My advice is to not use this at all.
Upvotes: 8
Views: 326
Reputation: 34130
The way Int:D(Any)
works in Rakudo is by creating a multi candidate that accepts Any
, converts it to an Int, and uses the result to call your original subroutine.
If you instead do that yourself, you can have more control over how it works.
proto sub foo ( Int:D() $n ) {*}
multi sub foo ( Any:D $n ) {
my $i = try $n.Numeric.narrow;
if $i ~~ Int:D {
samewith $i
} else {
X::TypeCheck::Binding::Parameter.new(
# there are more arguments that should be added here
got => $n,
expected => Int:D(),
).throw
}
}
multi sub foo ( Int:D $n ) { put "Got <$n> of type {$n.^name}" }
Upvotes: 7
Reputation: 169723
One possible signature would be
Numeric() $ where Int
or, restricting to strings,
Numeric(Str:D) $ where Int
Upvotes: 8