in reply to Re: modular file scoping (updated)
in thread modular file scoping

Thank you indeed.

"....you're asking about the my %webpaths variable, and why your three functions in package DataBank can keep using it despite the execution of the file DataBank.pm having already finished?..."

Yes, the persisting STATE of %webpaths perplexing me somewhat!.

But I take your point about more persisting going on 'under the hood' than I generally pay attention to, and I am thankful indeed that mostly I have been spared the need to. I think my problem is just explicitness (which I tend to practise stringently in terms of style). I use 'our @EXPORT_OK = qw(subx suby subz)' a lot, of course, but in that very limiting way. And with 'my' can generally see exactly its scope within the same page (aka file) I am working on. Now I am wondering if the key factor for Perl retaining the state of %webpaths is due specifically to the precedence of calls in testme.pl to subs defined in DataBank.pm, and will therefore persist while testme.pl stays alive, even though other modules which altered the state of %webpaths are themselves long dead? (That at least would satisfy my need for order!)

I am most grateful of the time and effort you put into an erudite explanation, which I really will have to study in some depth (and perhaps open my eyes to possible trajectories I have hjtherto been too nervous to explore).

Thanks again, haukex!

Replies are listed 'Best First'.
Re^3: modular file scoping
by haukex (Archbishop) on Oct 13, 2017 at 08:17 UTC
    Now I am wondering if the key factor for Perl retaining the state of %webpaths is due specifically to the precedence of calls in testme.pl to subs defined in DataBank.pm, ...

    Yes, that's it (I assume you meant s/precedence/presence/). From Persistent variables with closures:

    Unlike local variables in C or C++, Perl's lexical variables don't necessarily get recycled just because their scope has exited. If something more permanent is still aware of the lexical, it will stick around. So long as something else references a lexical, that lexical won't be freed--which is as it should be. You wouldn't want memory being free until you were done using it, or kept around once you were done. Automatic garbage collection takes care of this for you. ... If declared at the outermost scope (the file scope), then lexicals work somewhat like C's file statics. They are available to all functions in that same file declared below them, but are inaccessible from outside that file. This strategy is sometimes used in modules to create private variables that the whole module can see.

    That "something more permanent" are the subs, and I'll get more into why those are "more permanent" below.

    ... and will therefore persist while testme.pl stays alive, even though other modules which altered the state of %webpaths are themselves long dead?

    Well, obviously it's a bit more complicated than "dead" and "alive" :-) Especially in dynamic languages like Perl, where the lines between the traditional "compile time" and "run time" can be blurred - you can run code at compile time with BEGIN and use, and compile code at runtime with eval, do, and require. In this case, consider what is going on when you write use DataBank; (or in your case use Importer 'DataBank';, which as I understand it is equivalent): During the compliation of testme.pl, when the compiler encounters the line use DataBank;, basically it will immediately compile and execute all of the code in DataBank.pm (plus the import/export code, the details of which I'll skip over for now), so when the line use DataBank; finishes compiling, the code in DataBank.pm will have finished executing (and all of its scopes have ended).

    Now the code in DataBank.pm includes statements like sub set_webpath { ... }. The important thing to keep in mind is that this does not actually run the code inside the sub, all it does is install that code into the symbol table under the names &DataBank::set_webpath and via the import/export mechanism also as &main::set_webpath, so that it can be run later, when other code says set_webpath(...). So I hope it's obvious that the subs from DataBank.pm need to stick around until after it has finished compiling and executing, because otherwise testme.pl couldn't call those functions, and the entire concept of modules exporting functions would break down. And since those functions need the %webpaths variable, it makes sense to keep that around as well, as described above.

      Progress ....

      Yes, I agree that 'Dead or Alive' is too simplistic (except to the extent for example that once the testme.pl file exits and its namespace is taken down all possibilities of persistence finally disappear, but not so other sub-files that may be brought into the app as 'helpers').

      My original misreading of the very doc you quote "Unlike local variables in C or C++, Perl's lexical variables ..." contributed to my confusion where it goes on to say "... If declared at the outermost scope (the file scope), then lexicals work somewhat like C's file statics. They are available to all functions in that same file declared below them, but are inaccessible from outside that file...", which I can now de-confuse for myself by adding '..inaccessible from outside.. EXCEPT IF REFERRED TO BY AN EXPORTED SUB!'

      So my explanation to myself now goes along the lines: "by referring to its own file-scoped lexicals, a module's exported subs become agents to the persisting state of those lexicals, available to any importer of those subs". Would you agree?

      "...(or in your case use Importer 'DataBank';, which as I understand it is equivalent)"... Yes, it just pre-empts Exporter's (again confusing) need to otherwise firstly establish an Import function for the caller, some of that oblique wizardry we referred to earlier, (and cannot really do without as you rightly say, but better kept 'under the hood' as much as possible perhaps?).

      Thanks again.

        '..inaccessible from outside.. EXCEPT IF REFERRED TO BY AN EXPORTED SUB!'

        Yes, I think that's what the first part of the sentence is trying to say: "They are available to all functions in that same file declared below them". Perhaps this helps: "inaccessible" does not mean "have gone out of scope" or "have stopped existing":

        foo(); sub foo { my $foo = "foo"; bar(); print $foo; } sub bar { print $foo; # doesn't work! }

        Here, I hope it's obvious that $foo is very much alive and well during the call to bar(), it is also still in scope inside sub foo, but sub bar has no way of accessing it!1 Think of sub foo and sub bar as being two different files (remember "file scope" is basically just another lexical scope), and I hope then the meaning of "but are inaccessible from outside" becomes clear.

        So my explanation to myself now goes along the lines: "by referring to its own file-scoped lexicals, a module's exported subs become agents to the persisting state of those lexicals, available to any importer of those subs". Would you agree?

        Yes, that's a good way to put it, keeping in mind the above distinction between "available/inaccessible" vs. having gone out of scope. Personally, I think it's easiest to think about it in terms of references, although in this case not the hard references described in perlref, but instead the "references" described in perlsub that I quoted before: "If something more permanent is still aware of the lexical, it will stick around." Since the subs in DataBank.pm refer to %webpaths, it is kept around.

        (Note: Conceptually, this is like closures.)

        Importer ... some of that oblique wizardry we referred to earlier

        I wrote a bit about the export/import mechanism here: "... the mechanisms that Perl uses for modules and pragmas are actually relatively transparent in that there isn't a ton of magic involved, and one can see all the moving parts." (most of that thread is a little OT to this one since it's about the lexical effect of pragmas, but perhaps the simplified description of export-/importing in that node is helpful).

        1 Ok, this is Perl, which likes to give you enough rope to shoot yourself in the foot if you want it, so every rule has an exception, but using that module should be considered dark magic to be used for debugging only.