fleetingflicker has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

I have a curious case of using "my" in a sub that I couldn't quite think it through.
I am just wondering if anyone can lend me some perl wisdom on this.

sub foo{ my $var = shift; my $href = shift; my @array = $href->{pass_in} if defined $href and exists $href->{pass_ +in}; push @array, $var; print "contents : @array"; } foo(1); foo(2);
output : contents : 1 contents : 1 2

So my question is that why @array is behaving like a static variable in C language here?

Replies are listed 'Best First'.
Re: A curious case of of my()
by moritz (Cardinal) on Apr 05, 2011 at 09:23 UTC
    Quoting perlsyn:
    NOTE: The behaviour of a "my" statement modified with a statement modifier conditional or loop construct (e.g. "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.

    (emphasis partially mine)

Re: A curious case of of my()
by JavaFan (Canon) on Apr 05, 2011 at 09:40 UTC
    So my question is that why @array is behaving like a static variable in C language here?
    It's an artifact of the implementation (and in particular, with an optimization). my has compile-time and run-time effects, and due to the if statement, not all run-time effects happen. As pointed out before, don't rely on it - it may not work like this in the future. And it may not work as expected now either - it won't act like a static C variable if you use recursion:
    #!/usr/bin/perl use 5.010; use strict; use warnings; sub foo { my $x if 0; say ++$x; } sub bar { my $x if 0; say ++$x; bar() if @_; } foo; foo; bar(1); __END__ Deprecated use of my() in false conditional at ./x line 10. Deprecated use of my() in false conditional at ./x line 15. 1 2 1 1
    Note that the second time bar() is called (due to recursion), you do get a different $x - unlike the foo() case.

    Since 5.10, there is a warning on my $foo if 0; (at compile time). People have argued there should always be a warning on any my $foo if EXPR or my $foo = EXPR1 if EXPR2; usage, but that hasn't happened, and isn't happening in blead either.

    Also since 5.10, if you want a static variable, you can use the state keyword.

      Thanks JavaFan and LanX.
      Points on compile-time and run-time difference are well taken.
      BTW, in my original code, I do use strict/warning, and knew that separating the declaration and condition statement makes it work as expected. But still knowing a little more about what is going on under the hood is more satisfactory.
Re: A curious case of of my()
by Anonymous Monk on Apr 05, 2011 at 08:38 UTC
Re: A curious case of of my()
by ikegami (Patriarch) on Apr 05, 2011 at 15:17 UTC

    It's quite simple: Using a my variable without having first executed the my is a bug. By braking that rule, you break the "fourth wall" between the promised behaviour of my and its optimised implementation.

    my variables are created at compile time. Executing a my places a directive on the stack that will get executed on scope exit. The directive causes the variable to be cleared (if possible) or replaced (if a reference keeps it alive).

    If you want a static variable, use state.

      Thanks, I forgot about scope-exit behavior...

      Anyway I'm wondering why a post-fix if doesn't force a new scope, IMHO that would solve the problem in a consistent way:

      use strict; use warnings; $a=0; if ($a) { my $z=666; } my $y=666 if $a; print $y; # -> nothing print $z; # -> Global symbol "$z" requires explicit package na +me

      of course treating short circuit and in the same way could cause more complications.

      $a and my $x =666; print $x; # -> nothing

      At least this "it's a new scope" logic could be used to detect "my"-problems at compile-time and throw warnings.

      Cheers Rolf

      UPDATE: short-circuit and

        It wouldn't fix the following:

        foo() and my $x; foo() or my $x; foo() && my $x; foo() || my $x; foo() // my $x; foo() ? my $x : 1;

        Not to mention that some code relies on the current behaviour.

Re: A curious case of of my()
by kilocoder (Novice) on Apr 05, 2011 at 16:42 UTC
    $href is empty, since it is not passed into foo. hence @array ends up being a package variable and not a lexical variable.
    your code
    sub foo{ my $var = shift; my $href = shift; my @array = $href->{pass_in} if defined $href and exists $href +->{pass_in}; push @array, $var; print "contents : @array\n"; } foo(1); foo(2);
    This gives
    contents : 1
    contents : 1 2

    However, commenting out the unused href code and declaring array as a lexical like:

    sub foo{ my $var = shift; my @array; #my $href = shift; #my @array = $href->{pass_in} if defined $href and exists $hre +f->{pass_in}; push @array, $var; print "contents : @array\n"; } foo(1); foo(2);
    gives

    contents : 1
    contents : 2

    I hope for nothing. I fear nothing. I am free.
      $href is empty, since it is not passed into foo. hence @array ends up being a package variable and not a lexical variable.

      As others have pointed out, that's wrong. my creates lexicals at compile time. It doesn't have a our fallback. The 'if' modifier with a false condition inhibits the opcode which clears the lexical at runtime.

      our @array; # package vaiable @array = qw( foo bar ); sub foo{ my $var = shift; my $href = shift; my @array = $href->{pass_in} if defined $href and exists $href +->{pass_in}; push @array, $var; print "contents : @array\n"; } print "outer scope - contents : @array\n"; foo(1); foo(2); print "outer scope - contents : @array\n"; __END__ outer scope - contents : foo bar contents : 1 contents : 1 2 outer scope - contents : foo bar

      As you can see, the variable @array inside the sub is a lexical.

A reply falls below the community's threshold of quality. You may see it by logging in.