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

After years of sloppiness I'm finally using 'strict'. I ran into some unexpected behaviour while adding 'use strict' to a working program.

The main part of the program is a foreach loop on a list. To save myself passing the value of the loop variable to a lot of subs, I want the loop variable to be global.

I thought that 'my $loop_var' at the start of the program would make it's scope the entire file, but that's not happening. Example:

use strict; my @colors = qw ( red green blue ); my $color; my $light = 'on'; foreach $color (@colors) { print "\$color is '$color' and \$light is '$light' in loop, "; print_color(); } #### sub print_color { print " in sub values are '$color' and '$light'\n"; }

gives:

$color is 'red' and $light is 'on' in loop, in sub values are '' and +'on' $color is 'green' and $light is 'on' in loop, in sub values are '' an +d 'on' $color is 'blue' and $light is 'on' in loop, in sub values are '' and + 'on'

$light works as I expected, but $color doesn't make it to the subroutine. If I use 'our $color' instead of 'my $color', I get the expected results.

I'm trying to understand why the foreach loop appears to narrow the scope of $color when using 'my,' but not when using 'our.' Any explanations?

Thanks,

Queeg

Replies are listed 'Best First'.
Re: strict, scope, my and foreach - not behaving as expected
by dave_the_m (Monsignor) on Jun 09, 2004 at 12:31 UTC
Re: strict, scope, my and foreach - not behaving as expected
by davido (Cardinal) on Jun 09, 2004 at 16:52 UTC
    foreach causes $color to be an alias for $colors[0], $colors[1], and $colors[2]. In other words, if you were to assign "pink" to $color on each iteration through the loop, @colors would be filled with 'pink', 'pink', and 'pink'. But the lexical $color, outside of the loop, would remain undefined. This is because of the fact that $color, inside the loop, is a localized alias to the elements of the list you're iterating over. It just happens to be localizing the lexical $color. But when the loop exits, the localized effects are dropped, and $color reverts back to its original self.

    The subroutine is defined outside of the loop, and therefore sees the version of $color that exists outside of the loop, and doesn't know about the localized aliases that are taking place inside the loop.

    This is a somewhat sloppy version of a closure.

    The proper way to pass data into a subroutine is as a parameter. You will find that if you rewrite your snippet with that in mind, your results will be more in keeping with what you predict them to be.

    use strict; my @colors = qw ( red green blue ); my $color; my $light = 'on'; foreach $color (@colors) { print "\$color is '$color' and \$light is '$light' in loop, "; print_color( $color, $light ); } #### sub print_color { my ( $clr, $lite ) = @_; print " in sub values are '$clr' and '$lite'\n"; }

    Also, if you want to preserve the last value of the loops iterator variable, you should play it safe by copying it to a variable that exists at greater scope, that hasn't been a part of the localized aliasing process.


    Dave

      Thanks, Dave.

      To answer some previous comments,

      Why modify an existing program? I want to learn how to 'use strict' (to catch my tpyos) and to scope properly. This is a small, manageable program, and it's easier to understand and correct my mistakes now than after it grows. It's going to be part of a larger project that might even generate <gasp> income. So I want to write it well.

      I'd like to ask 'why' too; why make the iteration variable global? Laziness. There are a few variables (including the outermost loop variable) that all the subs use , and rather than code and keep track of all the passing of parameters to subroutines, I made them global. Of course, I've spent more time trying to understand this issue than I ever would have passing parameters, but hey, that's how you learn.

      What confused me was this: If I satisfy strict with

      foreach my $color (@colors) { ... }
      then I expect $color to be local to the loop. When
      my $color;
      outside of the loop satisfied strict for the loop variable $color I expected $color would be available outside of the loop during iteration. It wasn't.

      There's still one question unanswered - if I use 'our $color' instead of 'my $color', the subroutine can see $color. I know that 'our' scopes globally, and I thought that 'my' at the beginning of the file, outside of any block, would scope to the whole file (effectively global), but it doesn't. So why are they different?

      Thanks,

      Queeg

      use strict; my @colors = qw ( red green blue ); our $color; # our makes it work, my doesn't foreach $color (@colors) { print "\$color is '$color' in loop, "; too_lazy_to_code_passing_parameter(); } #### sub too_lazy_to_code_passing_parameter { print " in sub value is '$color'\n"; } $color is 'red' in loop, in sub value is 'red' $color is 'green' in loop, in sub value is 'green' $color is 'blue' in loop, in sub value is 'blue'
Re: strict, scope, my and foreach - not behaving as expected
by Abigail-II (Bishop) on Jun 09, 2004 at 13:07 UTC
    I ran into some unexpected behaviour while adding 'use strict' to a working program.
    I highly doubt that adding 'use strict' lead to unexpected behaviour. From the description of the problem, it looks to me that you added a 'my' that caused to problem you experienced. 'my' and 'strict' are two different things. You can use lexical variables without having any strictness enforced (just like you can drive the speed limit even without the police nearby!), and you can have strictness enforced without having any lexical variable in your program.

    But what I really wanted to ask is Why? What's the point of adding 'use strict' to a working program? 'use strict' is a development tool. A useful one, but starting to use it on a working program adds, uhm, nothing. OTOH, it might prevent your program from compiling, inviting you to modify the program, which might cause all kinds of bugs to creep in.

    Abigail

      Modifying the program is not inherently harmful, and in my opinion, modifying code that does not run under strict is usually a good idea. Not that all programs should be made to run under strict, but the fact is that it prevents a lot of subtle bugs that might not come up in normal debugging at all.

      As to the problem in the original question, I'd like to ask 'why' too; why make the iteration variable global? Call-by-value is really not that expensive, and if you feel it is, use $_[0].

        Modifying the program is not inherently harmful,
        Not inherently harmful? I'd say a large percentage of bugs in programs comes from the fact someone modified the program. That's my point. If something is not broken, don't fix it.
        but the fact is that it prevents a lot of subtle bugs that might not come up in normal debugging at all.
        It was given that the program was working. I assume that the OP doesn't mean "it has bugs" when he writes "working".

        Adding strictness on a working program is a bit like rebuilding the Egyptian pyramids because originally the construction workers weren't wearing safety harnesses.

        Abigail

Re: strict, scope, my and foreach - not behaving as expected
by periapt (Hermit) on Jun 10, 2004 at 12:17 UTC
    To answer your last question about. The use of "our" keeps the variable in scope and does not restrict $color to the current lexical scope. Basically, when used outside of any brace delimited block, it lasts through the end of the compilation unit ( Learning Perl p133) which, in your case, should be the entire program. Consequently, when you are inside the loop, $color refers to the variable you expect it to and not the locally scoped variable that occurs with "my". "our" also allows the subroutine print_color() to access the variable which the "my" declaration would not since lexical variables are hidden from any subroutine called from their scope.

    PJ
    We are drowning in information and starving for knowledge - Rutherford D. Rogers
    What good is knowledge if you have to pull teeth to get it - anonymous