http://qs321.pair.com?node_id=298425


in reply to Re: Re: Re: Re^3: Perl to Ruby translator?
in thread Perl to Ruby translator?

I was going to tell you why it's not a closure, but fortunately I checked first, and of course, you are right.

I doubt your central assertion that this sort of thing happens all the time though. How many times do you see named subroutines declared inside loops? Almost never. And I have never heard of anyone accidentally creating a closure.

Personally, I find using closures a liberating experience, and since they make some hard things easy, actually reduce bugs.

Replies are listed 'Best First'.
Re: Re: Re: Re: Re: Re^3: Perl to Ruby translator?
by diotalevi (Canon) on Oct 10, 2003 at 21:33 UTC

    You're quite incorrect. In most cases perrin's example would have been file scope. That sort of accidental closure is excessively common. So much so that I seem to recall there being a sort of relaxing about the rules of scoping for file scoped my() lexicals but I don't recall what it was or where I heard it.

Re: Re: Re: Re: Re: Re^3: Perl to Ruby translator?
by perrin (Chancellor) on Oct 10, 2003 at 21:42 UTC
    Who said this was in a loop? This is a CGI script, which exhibits this problem when run in any persistent environment like mod_perl, PerlEx, etc. These are a very common target for Perl code, so this comes up all the time. I have helped several people with this exact problem on PerlMonks, and many more on the mod_perl list. They never believe me when I tell them it's a closure.

    It happens in other situations too, but this is the most common one I've seen.

      Well, now I understand the mechanics behind it becoming a closure, but why on earth would perl make it a closure? That doesn't make any sense to me. Why wouldn't the sub just use whatever $q it can find in the closest scope to it?

        That's exactly what makes it a closure.

        Lexical variables live in their own symbol tables (that's not what they're called in Perl 5, but it's the concept: just a mapping of a name to the data structure that represents the appropriate variable container). They nest — that's why you can write code like this:

        my $foo = 'outside'; { my $foo = 'inside'; print "Inside: '$foo'\n"; } print "Ouside: '$foo'\n";

        If you dump the optree with something like B::Concise, you'll see enter, leave, and pad operations. The enter and leave ops govern which symbol table (scratch pad or lexical pad, in Perl 5 terms) is active. Think of it like a stack and you'll get it. The pad operations look up a symbol in the scratch pad.

        Subroutines (anonymous and named) are represented by data structures called CVs (basically, Code Values). Every CV is attached to a lexical symbol table. That's just a place to store the lexical variables declared and used within that subroutine.

        To handle the nesting issue, there has to be a way to look outward from inner scopes. If there's no $foo in the tightest, most innermost scope, look outward, one scope at a time, to find the nearest appropriate $foo. (This is complicated a little bit by global variables, which are handled with a different symbol table implementation in Perl 5. There also appears to be variable analysis because of the pad ops, not glob ops. Insert handwaving here; I really don't want to read that code right now.)

        The code example you're asking about is a closure so that nested variable scopes will work. Or, put the way it actually happened, because nested variable scopes had to work, closures were possible.

        It is not a problem when run as is, because it does not really create a closure when the lexical is declared at the top level outside of any subroutine (I may be wrong, but it's at least a closure that doesn't create any problems...yet). So this works ok:
        use strict; use warnings; my $foo = 'foo'; bar(); $foo = 'bar'; bar(); sub bar { print "$foo\n"; }
        The problem comes when the script is run under Apache::Registry, because it turns the script into something like this:
        sub foo1234 { use strict; use warnings; my $foo = 'foo'; bar(); $foo = 'bar'; bar(); sub bar { print "$foo\n"; } } # Execute once foo1234(); # Execute twice foo1234(); OUTPUT: foo bar bar bar
        This gives you '$foo will not stay shared' warnings, and the result is that '$foo' does not change after the first execution of the subroutine that Apache::Registry wraps your script in. And mod_perl executes this subroutine over and over again (that's how Apache::Registry keeps your script persistent within mod_perl).