I've just spent a bunch of time debugging an issue that I've traced back to wantarray()
. I've distilled it down to this test case. (Ignore the fact that $!
won't have any useful info in this scenario). What I'd like to know is why wantarray
does not think it's being called in LIST context in the second example:
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
{
my ( $one, $two ) = foo();
is( $one, 'a', 'just foo' );
is( $two, 'b', 'just foo' );
}
{
my ( $one, $two ) = foo() || die $!;
is( $one, 'a', '|| die' );
is( $two, 'b', '|| die' );
}
done_testing();
sub foo {
return wantarray ? ( 'a', 'b' ) : 'bar';
}
The output of this test is:
$ prove -v wantarray.pl
wantarray.pl ..
ok 1 - just foo
ok 2 - just foo
not ok 3 - || die
not ok 4 - || die
1..4
# Failed test '|| die'
# at wantarray.pl line 15.
# got: 'bar'
# expected: 'a'
# Failed test '|| die'
# at wantarray.pl line 16.
# got: undef
# expected: 'b'
# Looks like you failed 2 tests of 4.
Dubious, test returned 2 (wstat 512, 0x200)
Failed 2/4 subtests
Test Summary Report
-------------------
wantarray.pl (Wstat: 512 Tests: 4 Failed: 2)
Failed tests: 3-4
Non-zero exit status: 2
Files=1, Tests=4, 0 wallclock secs ( 0.03 usr 0.01 sys + 0.02 cusr 0.00 csys = 0.06 CPU)
Result: FAIL
Because it's not being called in list context. ||
imposes scalar context on its left hand side, and its left hand side in this case is the expression foo()
.
You should write instead
my ( $one, $two ) = foo() or die $!;
The or
operator binds even more loosely than the assignment operator, so now its LHS is the entire expression my ($one, $two) = foo()
, and foo
's context is determined by the list assignment operator, and everyone is happy.
The reason is due to the precedence of the ||
operator. Your statement is basically parsed like this:
my ( $one, $two ) = ( foo() || die $! );
The ||
puts its operands in scalar context in this case.
On the other hand, if you change ||
to or
, which has much lower precedence, your tests will pass.
Logical or (||
) is a scalar operator, so using it will force the evaluation of foo()
to become scalar context.
Try this:
my @a = 10 .. 100;
print(@a || 2), "\n";
# prints 91
You'd expect this to print the elements in @a
if it wasn't because the array has been evaluated in scalar context.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With