Type::Params: unexpected error when die'ing in goto_next in a multiple signature
djerius opened this issue · 2 comments
Type::Tiny 2.004000
I'm trying to handle a legacy API issue by using multiple signatures. The old API is
foo( $hashref, %options )
i.e., first is positional, rest are named.
The new API is
foo( head => $hashref, %options );
i.e., all are named.
In order to transparently convert from the first to the second, I'm doing this:
sub goto_next {
$_[1]{head} = $_[0];
return $_[1];
}
signature_for foo => (
multiple => [ {
# legacy
goto_next => \&goto_next,
head => [Str],
named => [ uri => Optional [Any], head => Optional [Any] ],
},
{
# new
named => [ head => Str, uri => Optional [Any] ]
},
],
);
sub foo { p @_ }
I need to provide the head
parameter to the named options for the legacy API otherwise the constructed parameter class doesn't have a head
attribute.
This however leaves open the possibility that this call:
foo( $head_value, head => $another_head_value );
could arise if someone tweaked %options
to include head
and forgot to fix the actual call.
So I figured I could catch this via:
sub goto_next {
die "legacy API: do not supply a named 'head' parameter"
if $_[1]->has_head;
$_[1]{head} = $_[0];
return $_[1];
}
But this call
foo( 'foo', head => undef );
results in a rather unexpected error:
$ perl boom.pl
Use of uninitialized value in pattern match (m//) at ../5.36/lib/perl5/site_perl/5.36.1/Error/TypeTiny.pm line 61.
Use of uninitialized value in pattern match (m//) at ../5.36/lib/perl5/site_perl/5.36.1/Error/TypeTiny.pm line 61.
Parameter validation failed at file? line NaN.
While debugging, I reverted to a similar approach with a single signature, i.e.
signature_for foo => (
goto_next => \&goto_next
head => [Str],
named => [ uri => Optional [Any], head => Optional [Any] ],
);
With the result that the call
foo( 'foo', head => undef );
results in the expected outcome:
$ perl tst.pl
legacy API: do not supply a named 'head' parameter at tst.pl line 9.
BTW, this is rather low priority, as there's a simple (rather less convoluted) workaround:
sub foo (@args){
state $signature = signature( named => [ head => Str, uri => Optional [Any] ] );
# support legacy API: foo( $head, %options )
if ( @args %2 ) {
my $head = shift @args;
my %arg = @args;
die "legacy API: do not supply a named 'head' parameter"
if exists $arg{head};
$arg{head} = $head;
@args = %arg;
}
my $opt = $signature->( @args );
p $opt;
}