2

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?

3
  • ah, you try to find bug in Perl? do you really understand what Perl understand in your code?;) I think that assignment inside lazy if-clause (do{my $var = value;}if value); should cause a problem Commented Aug 15, 2014 at 18:07
  • 2
    just don't do my ... if. but what actually happens (currently; it is flagged in the doc as subject to change) is that my has both a compile-time an a run time effect; the if can skip the run time effect, keeping perl from knowing it needs to reinitializing the variable at end of scope. Commented Aug 15, 2014 at 18:15
  • 1
    in such cases, try perlcritic too: it says: Variable declared in conditional statement at line 22, column 5. Declare variables outside of the condition. (Severity: 5) (and the same for the line 23) Commented Aug 15, 2014 at 18:22

1 Answer 1

7

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Very interesting. I had other reasons for formatting the code this way that aren't apparent in my example, although looking over it I'm... not sure what those reasons were anymore. Perhaps I'll get rid of it and see what breaks. Thanks for your reply :)
@JasonHamje, documentation ... you know what you should do ;)
@ysth I think he was making the point to Jason about why its good to document your own code. Since Jason said he had other reasons for formatting his code but cannot remember them. If he had documented them then he wouldnt have to remember.
@gaussblurinc; fair enough, fair enough :P
@ysth, uh, english sarcasm so simple.. uh, no, I mean that OP should read it in future

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.