4

I have a strange behaved (to Python programmer) subroutine, which simplified as the following:

use strict;
use Data::Dumper;
sub a {
  my @x;
  sub b { push @x, 1; print "inside: ", Dumper(\@x); }
  &b;
  print "outside: ", Dumper(\@x);
}
&a;
&a;

I found the result is:

inside: $VAR1=[ 1 ]
outside: $VAR1 = [ 1 ]
inside: $VAR1=[1, 1]
outside: $VAR1= []

What I thought is when calling &a, @x is empty array after "my @x" and has one element after "&b", then dead. Every time I call &a, it is the same. so the output should be all $VAR1 = [ 1 ].

Then I read something like named sub routine are defined once in symbol table, then I do "my $b = sub { ... }; &$b;", it seems make sense to me.

How to explain?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Forest Yang
  • 179
  • 5
  • 1
    Modern Perl seldom uses the `&` in front of a function name. – Jonathan Leffler Dec 08 '16 at 05:09
  • I just want to make &$b and &b, &a consistent. & in "&$b" is not optional if "my $b = sub { ... }". Anyway, so far just learned Perl for a month. – Forest Yang Dec 08 '16 at 05:18
  • 6
    If I add `use warnings;` (or use `-w` on the command line), I get the message: `Variable "@x" will not stay shared at sub23.pl line 8.`. Reminder: always use both `use warnings;` and `use strict;` (unless you know enough about Perl not to need that — I've only been using it for 20+ years so I know I don't know enough to risk it). – Jonathan Leffler Dec 08 '16 at 05:18
  • "*I've only been using it for 20+ years*" lol :-) I've been using Perl for 15 years but that result still surprise me! – Déjà vu Dec 08 '16 at 05:20
  • Almost a duplicate: http://stackoverflow.com/questions/4048248/variable-foo-will-not-stay-shared-warning-error-in-perl-while-calling-subrout/4048277#4048277 – mob Dec 08 '16 at 05:38
  • 1
    Side note: it is unusual to see a named sub inside a named sub. If this is real production code, you probably want to reconsider your approach – Zaid Dec 08 '16 at 06:26
  • 2
    N.B. `&b` or `&$b` without `()` are a special form of sub call that you almost never want to use. Do `&b()`/`&$b()` instead (or, as preferred by many, `b()`/`$b->()`). (This does not apply to method calls; empty parentheses can be left off those.) – ysth Dec 08 '16 at 07:18
  • For sub inside sub, I kind of like this "local" define, used once and discard. Did not realize such a different behavior between named and anonymous subroutines. @Zaid why not in production code ? are you saying "*named* sub inside a sub", but instead using "anonymous sub inside sub" is OK ? – Forest Yang Dec 08 '16 at 14:45
  • 2
    @ForestYang that's right, if it is a use-and-throw type of situation, there is little reason to give the inner sub a name, `my $s = sub { ... }; $s->();` is a more natural choice here. – Zaid Dec 08 '16 at 15:01

1 Answers1

12

As per the "perlref" man page:

named subroutines are created at compile time so their lexical variables [i.e., their 'my' variables] get assigned to the parent lexicals from the first execution of the parent block. If a parent scope is entered a second time, its lexicals are created again, while the nested subs still reference the old ones.

In other words, a named subroutine (your b), has its @x bound to the parent subroutine's "first" @x, so when a is called the first time, b adds a 1 to @x, and both the inner and outer copies refer to this same version. However, the second time a is called, a new @x lexical is created, but b still points to the old one, so it adds a second 1 to that list and prints it (inner), but when it comes time for a to print its version, it prints out the (empty) brand new lexical (outer).

Anonymous subroutines don't exhibit this problem, so when you write my $b = sub { ... }, the inner @x always refers to the "current" version of a's lexical @x.

Déjà vu
  • 28,223
  • 6
  • 72
  • 100
K. A. Buhr
  • 45,621
  • 3
  • 45
  • 71