[Wellington-pm] How to cure "variable will not stay shared?

Grant McLean grant at mclean.net.nz
Thu Nov 3 16:12:57 PDT 2011


On Fri, 2011-11-04 at 11:35 +1300, Lesley Longhurst wrote:
> Hi mongers,
> 
> So, I'm looking at some nasty legacy code left behind by Mordac. I added 
> "use warnings; use diagnostics" and got a pile of these warnings.  From 
> what I've read so far, it seems that they are caused by a subroutine 
> being defined inside a subroutine.

It sounds like you're on the right track there.

The problem results from the fact that Perl's "my" keyword has compile
time and runtime effects.  Consider this code snippet:

    sub outer {
        my($msg) = @_;
        my $now = localtime();

        sub print_message {
            my($message) = @_;
            print "$now $message\n";
        }

        print_message($msg);
    }


    outer('First message');
    sleep(1);

    outer('Second message');
    sleep(1);

    outer('Third message');
    sleep(1);

The inner 'print_message' sub is a closure around the $now variable
(this is set up at compile time).  But a new $now variable is created
each time outer() is called (this happens at run time) and the inner sub
is left pointing to the first $now.

>  The diagnostics text suggests making 
> the inner subroutine anonymous, but I don't think that's going to work 
> because it's called by name many times.

In my snippet I would do that by changing to:

    sub outer {
        my($msg) = @_;
        my $now = localtime();

        my $print_message = sub {
            my($message) = @_;
            print "$now $message\n";
        };

        $print_message->($msg);
    }

This works because the inner print_message sub is redefined each time
outer is called().

> Am I likely to make matters worse if I just take the inner sub and move 
> it outside the outer sub? Most of the variables are defined without "my" 
> and there is no other sub with the same name.

If the variables are defined without "my" then that's roughly equivalent
to defining them at the start of the script with "our", e.g.:

    our($now);

    sub outer {
        my($msg) = @_;
        $now = localtime();
        ...

Changing it to explicity use "our" might be the lowest risk approach to
fixing the problem.

Cheers
Grant




More information about the Wellington-pm mailing list