for() $DEITY's sake, why?

I used to think I had a reasonable grasp of Perl. Yesterday, I realised I didn’t even understand a basic foreach loop.

  my $val;
  my @values = qw( a b c );
  foreach $val (@values) {
    print $val, "\n";
  }
  print "[end] $val\n";

I reckoned that this should print:

  a
  b
  c
  [end] c

Instead, it prints:

  a
  b
  c
  Use of uninitialized value in concatenation (.) or string at foo.pl line 11.
  [end]

This confused me no end. But it’s actually documented behaviour. From perlsyn

The foreach loop iterates over a normal list value and sets the variable VAR to be each element of the list in turn. If the variable is preceded with the keyword my, then it is lexically scoped, and is therefore visible only within the loop. Otherwise, the variable is implicitly local to the loop and regains its former value upon exiting the loop. If the variable was previously declared with my, it uses that variable instead of the global one, but it’s still localized to the loop. This implicit localisation occurs only in a foreach loop.

Wow. You really do learn something new every day. I suspect that this is implementation behaviour that was documented post-fact, rather than designed that way.

2 Comments to for() $DEITY's sake, why?

  1. Ahhh… That does make sense. Thank you, I feel enlightened.

  2. It’s just a consequence of the fact that you can do this:

    my @foo = map { "foo$_" } 1..4;
    s/foo/bar/ for @foo;
    print "@foo\n";
    # output: bar1 bar2 bar3 bar4

    This works because the iterator is an alias for the value, not a copy, which is very much by design, not an implementation detail come feature. And it is in fact so incredibly useful in practice that I’ll happily live with the fact that it implies some extra legwork to preserve the last iteration’s iterator value past the loop. I only need that extremely rarely, and I think having to spell it out actually helps readability by alerting the reader that this particular behaviour was intended by the original programmer.

    To my knowledge this is actually unique about Perl. I wouldn’t have it any other way, and while it’s a very small thing, I always eventually find myself missing it in other languages.