in reply to Perl scoping not logical...?

I think adding use diagnositics; will help you understand this issue, and you can read more about the issue at http://www.perl.com/pub/a/2002/05/07/mod_perl.html:
(W) An inner (nested) named subroutine is referencing a lexical variable defined in an outer subroutine.

When the inner subroutine is called, it will probably see the value of the outer subroutine's variable as it was before and during the *first* call to the outer subroutine; in this case, after the first call to the outer subroutine is complete, the inner and outer subroutines will no longer share a common value for the variable. In other words, the variable will no longer be shared.

Furthermore, if the outer subroutine is anonymous and references a lexical variable outside itself, then the outer and inner subroutines will never share the given variable.

This problem can usually be solved by making the inner subroutine anonymous, using the sub {} syntax. When inner anonymous subs that reference variables in outer subroutines are called or referenced, they are automatically rebound to the current values of such variables.

In your case I think the problem is the definition of sub do_pkg_need_from_Dist. Does it really need to be defined as a closure within process_need_from_Dist?

Update: Your use of the variable $new_pkg is very odd:

my $new_pkg; { sub do_pkg_need_from_Dist { ... $new_pkg = ...; # sets $new_pkg from the outer scope ... return $new_pkg; } $new_pkg = do_pkg_need_from_Dist(...); # sets $new_pkg again??? }
Declaring my $new_pkg; inside do_pkg_need_from_Dist should fix the error message about that variable.

Replies are listed 'Best First'.
Re^2: Perl scoping not logical...?
by perl-diddler (Chaplain) on Apr 27, 2008 at 17:01 UTC
    I wanted the closure to isolate the variables declared at the top of the closure, but it isn't really needed for the program nor the error -- i.e. removing the brace after "my $newpkg;" and before "my ($rq_nam, $rq_vr, $rq_op)" makes no difference in regards to the problem -- I get the same warning that perl isn't able to share the syntactically shared variables.

    Perhaps it's a limitation in the current perl or, at the least, a semi-bogus warning.

      The warning isn't bogus, and IMHO the correct action should be harsher: the compiler should halt when encountering this situation since you cannot use nested named subroutines to close over the outer variables in perl (or you can, but as you noticed it won't do anything remotely useful).

      Just use an un-named subroutine assigned to a variable instead.

      update: if you think about it for a bit, you might notice why using named nested named subroutines as closures isn't very useful anyway: it implies you're redefining the - global - inner subs at each call to the outer subs.

        Joost wrote: "... the correct action should be...the compiler ... halt when encountering this situation since you cannot use nested named subroutines to close over the outer variables in perl (or you can, but as you noticed it won't do anything remotely useful). Aye. I think we are seeing a similar problem, but coming to different conclusions about how to address the issue.

        I'm wanting to use certain "semantically-rich" features or behaviors that I'm familiar with, in Perl. As it has still has a strong root in its origin of being a simple scripting language, it doesn't currently support semantically(?syntactically?)-structured, non-global functions nor shared, "subroutine-private", but intra-subroutine, sharable variables between subroutines that share an enclosing scope, for the duration of the enclosed scope. Rather than taking the position that perl was never meant to do anything so complex, therefore, it should die on attempts to do such things, I tend toward enhancing the language, with useful constructs or behaviors that it could come to share with more traditional, compiled, high-level languages.

        While it is likely that either a property of "sub" or a different term (sublet? :-) ) could be used for sub routines that are lexically scoped in the same way "my"-labelled variables are. Perhaps it could be "so simple" as to use a syntax like: my sub foobar[(optionalproto)]{...}. (Dang, sorta liked sublets...).

        It's not like providing (allowing & supporting) "lexically-scoped" subroutines should be so considered so radical -- they are the norm in most languages. Perl, because of it's origins as a scripting language wasn't perceived to need such subtleties as lexical scoping when it was first developed, because it wasn't designed to be a general-purpose high-level compiler-like language. It didn't even have subroutines at one point. But... when it got them, it only implemented one global name space for subroutines that exists to this day. One can have syntactic division of subroutines within the global name space by prepending package-names, but it's not the same as lexically scoped subroutines.

        Don't you think it could be 'doable' -- at least, at first, within a simple case of subroutines being able to share local variables declared at the same level as the subroutines as is nearly the case here? I.e. the named subroutine is declared at the same level as variables it is accessing, with both being declared inside the same function.

        For a previous poster, I tried moving the nested subroutine definition out a level, -- removing the enclosing brackets -- making them at the same level. It seems like that level of nesting should be straight forward, no?

        Regarding your 'update' -- the fact that the nested subroutines are global isn't useful or desired behavior in this situation. I agree it is what the language has implemented at this point, but that doesn't mean a "my" keyword couldn't be added in front of a "sub" definition to simply allow those subroutines to only come into existence like their adjacent "my"-declared variables, no? It's a syntactic difference -- rather than forcing the user to use an anonymous subroutine with ref in a lexically scoped variable, wouldn't the code be more clear if I could use a named-lexically scoped subroutine name?

        I don't see that requiring the extra syntax of making the subroutine 'anonymous', and storing it in a variable, then calling it through a variable could possibly make its more clear for someone reading the code. Neither, IMPO, is putting the function outside of the area where it was intended to be used. That only invites the possibility that later, I or some maintainer might try to call the 'helper function' from a context it was not designed to support. By declaring it within the subroutine, it emphasizes that is a subroutine-context-dependent helper routine, no? :-)

        Do you think we could come up with a Perl "RFE" that could "swim"? :-)

        Linda

      I would structure your code this way:
      sub do_pkg_need_from_Dist { my ($Distdb, $rq_nam, $rq_vr, $rq_op, $Need_msg) = @_; my $newpkg; ... set $newpkg here ...; return $newpkg; } sub process_need_from_Dist { my $Need_msg = sub { ... }; my $newpkg; ... if ($rq_nam) { $newpkg = do_pkg_need_from_Dist(..., $Need_msg); } else { $newpkg = $Distdb->find_rpm_owning_file_to_pkg($need); if ($newpkg) { $newpkg->add(...); } else { Serious ...; } } }