Bitten by prototypes
I just spent the best part of an hour around a problem caused by the behavior of Perl prototypes.
I used the following test case to figure it out:
use Test::More tests => 1;
use Encode qw( encode decode );
sub u8l1 {
return encode('iso-8859-1', @_);
}
my $ola_u8 = decode('utf8', 'Olá');
my $ola_l1 = encode('iso-8859-1', $ola_u8);
is(u8l1($ola_u8), $ola_l1);
The output of prove x.t
is this:
t/x.t .. 1/1
# Failed test at t/x.t line 12.
# got: '1'
# expected: 'Ol?'
# Looks like you failed 1 test of 1.
t/x.t .. Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/1 subtests
The got: '1'
had me for quite some time. Until I changed the u8l1()
helper to this:
sub u8l1 {
return encode('iso-8859-1', $_[0]);
}
And it just works.
The problem is the definition of the Encode::encode()
function. It has a prototype like this:
sub encode($$;$)
So our @_
is interpreted in scalar context, and so evaluates to the number of parameters, 1.
I don't like it at all because it changes the standard Perl behavior of expanding lists. Its action at the distance. The fact that you cannot pass a single element list is also not mentioned in the documentation.
The only really useful use of Perl prototypes is using a &
as the initial char, that allows you to write a function that looks like some built-ins like sort or map, that take a anonymous sub as the first parameter.