So I have a situation where I have a function with two named arguments, but if both are used the function needs to die. I'm not sure if this is a bug, or if there's something intrinsic I'm not understanding about Perl variables.
Here's the simplified code:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
foreach my $number (1..5)
{
fooBar(foo => $number);
}
sub fooBar
{
my %args = (
foo => undef,
bar => undef,
@_
);
my $foo = $args{foo} if defined $args{foo};
my @bar = @{$args{bar}} if defined $args{bar};
print Dumper(\@bar);
if (defined $foo)
{
die('fooBar() received both a foo and a bar and didn\'t know which to use!') if @bar;
push(@bar, $foo);
}
print Dumper(\@bar);
return (\@bar);
}
# > $VAR1 = [];
# > $VAR1 = [
# > 1
# > ];
# > $VAR1 = [
# > 1
# > ];
# > fooBar() received both a foo and a bar and didn't know which to use! at ./example.pl line 27.
Here's my solution:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
foreach my $number (1..5)
{
fooBar(foo => $number);
}
sub fooBar
{
my %args = (
foo => undef,
bar => undef,
@_
);
my $foo;
my @bar;
$foo = $args{foo} if defined $args{foo};
@bar = @{$args{bar}} if defined $args{bar};
print Dumper(\@bar);
if (defined $foo)
{
die('fooBar() received both a foo and a bar and didn\'t know which to use!') if @bar;
push(@bar, $foo);
}
print Dumper(\@bar);
return (\@bar);
}
# > $VAR1 = [];
# > $VAR1 = [
# > 1
# > ];
# > $VAR1 = [];
# > $VAR1 = [
# > 2
# > ];
# > $VAR1 = [];
# > $VAR1 = [
# > 3
# > ];
# > $VAR1 = [];
# > $VAR1 = [
# > 4
# > ];
# > $VAR1 = [];
# > $VAR1 = [
# > 5
# > ];
My question is, why does this fix the problem? In the first example, the first time fooBar()
is called, @bar
is getting initialized despite failing the if condition. The second time through the loop, @bar
has somehow retained its information from the first loop, yet it doesn't complain about being reinitialized. It seems to me that either a) @bar
should be wiped after the subroutine finishes (which is what I was expecting), b) @bar
shouldn't be initialized after it fails the if defined
test, or c) @bar
should complain that it's being reinitialized the second time through the loop. I'm pretty confused here, is this just a bug?
This only conditionally creates a new variable, and can lead to strange to understand results:
my $foo = $args{foo} if defined $args{foo};
perldoc perlsyn has this to say:
NOTE: The behaviour of a my, state, or our modified with a statement modifier conditional or loop construct (for example, my $x if ... ) is undefined. The value of the my variable may be undef, any previously assigned value, or possibly anything else. Don't rely on it. Future versions of perl might do something different from the version of perl you try it out on. Here be dragons.
Don't try to define a $foo
variable; just use $args{foo}
. Or just do the assignment unconditionally; there's nothing wrong with assigning undef.