in reply to Re: Perl scoping not logical...?
in thread Perl scoping not logical...?

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.

Replies are listed 'Best First'.
Re^3: Perl scoping not logical...?
by Joost (Canon) on Apr 27, 2008 at 17:09 UTC
    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

        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.

        Certainly not. I'm only aware of Pascal having lexically scoped/nestable subroutines, likely Modula has inherited them. Many other languages only have one, global, namespace for subroutines - certainly, Python, Ruby, PHP, JavaScript, Visual Basic, C and C++ have it that way.

        In Perl, what you want is written in the following idiom:

        sub foo { my $bar = sub { print "In bar\n"; }; $bar->(); };

        This idiom is frequently used and if somebody cannot read it, they should just learn Perl. You could change into a syntactically different flavour if you're willing to trade lexical for dynamic scope:

        sub foo { local *bar = sub { print "In bar\n"; }; bar(); };
        It's not like providing (allowing & supporting) "lexically-scoped" subroutines should be so considered so radical...
        It's not radical, and it's been discussed on and off by the perl implementors for years, and generally (I think) thought of as a good idea, but it just hasn't been done, probably because it's not considered to be worth the time, especially since you can already use lexically scoped anonymous subs for all situations you'd use lexical named subs, with only minor extra syntax, and you can assign closures to globs to redefine global subs, and neither require excessive amounts of code (one additional my statement + one deref per call, and one assignment, respectively). In the mean time, named subroutines in perl are global, and bitching about it isn't going to help.

        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.
        It does make it very clear you're calling a lexically scoped subroutine, instead of a global one. Not a big advantage, though, I grant you, but perl will never be Scheme. Perl isn't pretty in some ways, but it does provide (sometimes clunky, but reasonable) ways to use almost any programming technique you want (with the exceptions of macros, which are just too clunky in perl - see source filters - to be truly useful, and good multi-processor/multi-node support, which still seems to be in its infancy as far as programming languages I know go - though Erlang seems to be a good step).

Re^3: Perl scoping not logical...?
by pc88mxer (Vicar) on Apr 27, 2008 at 17:59 UTC
    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 ...; } } }