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

I had wanted to store a summary of multiple calls to a sub in a module, and then get that summary out of the module at the end of the program. But I ran into the "closure issue".
Now two questions?
1)This prints 2, not 3 as I had hoped it would (because I used a named sub as a closure), but why don't I get a "stay shared" warning with either diagnostics or -w (I use ActiveState perl for win32)?
2) Is there a way to store the results of cumuative calls to a module sub in that module, or do the results have to be stored by the caller?
Thanks
use strict; use diagnostics; up(); up(); print get(); { my $foo = 1; sub up { $foo++; sub get {$foo} } }

Replies are listed 'Best First'.
(tye)Re: Why no stay shared warning?
by tye (Sage) on May 05, 2001 at 19:23 UTC

    To get the "won't stay shared" warning, you have to have something like:

    sub a { my $x; sub b { $x } }
    Your my $foo isn't inside a subroutine so you just have one lexical variable scoped to that bare block. If the my was inside your up subroutine, then each call to up would create a new instance of $foo and that would mean that your get subroutine wouldn't know which instances of $foo that you wanted it to use.

    Since the bare block is only ever executed once, your my only ever creates one instance of the variable so it "stays shared". When a my is compiled, it declares the variable (which leaves it set to undef). When the my is executed, it initializes the variables. Executing the my a second or subsequent time creates a new instance of the variable and then initializes that. Even if you require or use a module more than once, the code for the module is only compiled and executed once (but the code for subroutines in that module could be executed more times or zero times).

    The code you used would be a fine way to store module-specific results. If your block were in a different module, then it would be the module and not the caller storing them.

    Updated.

            - tye (but my friends call me "Tye")
      It seems that my $foo only creates a place for sub up to store values between calls. Why does initializing it to 20 not have an effect?
      Thanks
      use strict; use diagnostics; up(); up(); print "getfoo=",get(); { my $foo = 20; sub up { print "subup=$foo\n"; $foo++; sub get {$foo} } } prints: subup= subup=1 getfoo=2
        The reason is because $foo = 20 doesn't occur until after you've called all those functions.

        DECLARATION occurs at compile-time. ASSIGNMENT happens at run-time. Different things. my $foo = 20 has a compile-time effect (my) and a run-time effect ($foo = 20).

        For your desired action, you'd need something like:
        my $foo; BEGIN { $foo = 20 }


        japhy -- Perl and Regex Hacker
Re: Why no stay shared warning?
by chipmunk (Parson) on May 05, 2001 at 19:54 UTC
    At the point where you print the value of $foo, it really is 2, not 3.

    Look carefully at your code... What order will each statement be executed in? The calls to up() and get() are actually executed before the initialization of $foo to 1.

    In order to get the result you want, you need to make sure that the $foo = 1 line is executed before any of the subroutine calls. One way to do this is:

    use strict; use diagnostics; up(); up(); print get(); { my $foo; BEGIN { $foo = 1; } sub up { $foo++ } sub get { $foo } }
Re (tilly) 1: Why no stay shared warning?
by tilly (Archbishop) on May 06, 2001 at 05:55 UTC
    Why is get defined inside of up?

    Anyways, in addition to the other suggestions that you have been offered, I tend to use lazy memoization as follows:

    { my $foo; sub up { unless (defined)($foo)) { $foo = 1; } $foo++; } sub get { $foo; } }
    And now the initialization happens when the subroutine is called. (This is a very handy pattern when the variable takes some work to create - eg for a database handle - and you may or may not need it.)
Re: Why no stay shared warning?
by merlyn (Sage) on May 05, 2001 at 21:05 UTC
    The normal idiom for a "static local" variable is to declare it inside a BEGIN block, and put the acccessor subroutines within that same block. Just add BEGIN to your block, and you're done!
    up(); up(); print get(); BEGIN { my $foo = 1; sub up { $foo++; sub get {$foo} } }
    This is needed because you need the block executed once in order for the variable to go into and then out of scope, forming a closure with your two named subroutines.

    -- Randal L. Schwartz, Perl hacker

      Note, a BEGIN block isn't necessary -- a bare block is fine as long as it is defined prior to the sub calls. Also, a BEGIN block may not always be the appropriate mechanism:

      #!/usr/bin/perl -w use strict; use Getopt::Std; my %opt; getopt('n',\%opt); BEGIN { my $foo = $opt{n}; sub up { $foo++; sub get {$foo} } } up(); up(); print get();

      Now, if you comment out the BEGIN line above it will work as one might have expected. Not that I think this might be a common problem, but I thought it might be worthwhile to highlight the difference.