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

Undeterred by the proliferation of 'lazy' modules on CPAN, I feel it's time to inflict my own lazy module on the world. Since I'm too lazy to pass around references and de-reference them all over the place, I've had to learn to pay very, very close attention to when I'm using an alias of a value, and when I'm using a copy of it. In particular, I've been twisting and stretching subroutine calls and fors to avoid copying. Now, however, I'm stuck. I'd like a subroutine that accepts a single scalar value as an argument, and returns a function that returns an arrayref to … err, that's confusing. I want a function prepend such that prepend($a)->($b) returns $return = [ $a, $b ], and that's easy to achieve; but I also want $return->[0] = 3 (say) to change $a, and that seems harder. I eventually came up with
sub prepend { for ( @_ ) { return sub { return sub { \@_ }->($_, @_); }; } }
Now, if I write my $return = prepend_to(my $a)->(my $b), then $return->[1] = 3 changes $b, but $return->[0] = 3 does not change $a. This confuses me, since it seems that $a is only ever getting passed around as part of a subroutine call or a for loop, neither of which should be making a copy.

The strange thing is, if I change the for invocation to for my $a ( @_ ) (and the $_ later to $a), then everything works just as I'd expect. My question is: Why? Does sub compile in the value of $_, but look in its pad for a named variable like $a; or is it something more subtle?

P.S. The ‘too lazy’ in the title refers to the fact that I know I could just do this my capturing a reference to $_[0] early on in prepend and then de-referencing it in the subroutine, but I'd rather avoid doing all that hard work by nesting subs 7 levels deep. :-)

Replies are listed 'Best First'.
Re: Too lazy to be lazy
by ikegami (Patriarch) on Jul 29, 2009 at 22:25 UTC

    When the anon sub instance is created, it is associated with the variables that existed then.

    When you use for my $x (or for my $_ in 5.10+), a new $x (or $_) is created for every pass of the loop. When the anon sub instance is created, it is associated with the iterator variable that existed then, a variable that was only used for that loop pass.

    When you use for $_, for $_ or for $pkg, the loop simply reuses the existing variable. When the anon sub instance is created, it is associated with the iterator variable that existed then, a package variable whose value will change once the loop pass ends.

    (Technically, package vars aren't captured at all. But unless you go mucking with the symbol table, the behaviour is the same as described.)

    sub mk_pkg { for our $pkg (@_) { return sub { $pkg } } } sub mk_lex { for my $lex (@_) { return sub { $lex } } } our $pkg = "XXX"; print mk_pkg('abc')->(), "\n"; # XXX print mk_lex('abc')->(), "\n"; # abc

    What was confusing me was why, in what seemed to be otherwise identical code, $a was capturing an alias inside the nested subroutine, but $_ was not.

    $_ changed value. $a didn't. $a just became anonymous at the end of the loop pass.

      Thanks very much—I forgot that $_ is a package variable.
Re: Too lazy to be lazy
by Anonymous Monk on Jul 29, 2009 at 20:24 UTC
    I think it is because @_ is magic
    my @foo = 1 .. 2; sub modit { $_[0] .= ' magic ' } print modit(@foo),"\n"; print "@foo\n"; __END__ 1 magic 1 magic 2
      I know about, and am relying on, the aliasing semantics of function calls and @_. What was confusing me was why, in what seemed to be otherwise identical code, $a was capturing an alias inside the nested subroutine, but $_ was not.