in reply to Re^3: modular file scoping
in thread modular file scoping

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.

Replies are listed 'Best First'.
Re^5: modular file scoping
by haukex (Archbishop) on Oct 14, 2017 at 07:29 UTC
    '..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.

      Personally, I think it's easiest to think about it in terms of references

      In fact, in the following you can see how Perl uses the same reference counting mechanism as for hard references. Hopefully this will now put your mind at ease :-)

      #!/usr/bin/env perl use warnings; use strict; # Debugging aids use Devel::Refcount 'refcount'; use Sub::Delete 'delete_sub'; { # This will act like a normal hash except it will print # "DESTROY" when it is destroyed (garbage collected). package DebugHash; BEGIN { require Tie::Hash; our @ISA = ('Tie::StdHash'); } sub DESTROY { print "DESTROY\n" } } { # Lexical scope for "my %hash". my %hash; # +1 reference to %hash (0+1=1) BEGIN { tie %hash, 'DebugHash' } # tie to debugging aid # Note: We'll use $hash{c} as a counter for debug output. # Apparently BEGIN{} blocks cause +1 reference to %hash (?) # Note: refcount()-1 since the argument \%hash is +1 reference. BEGIN { print ++$hash{c},": ",refcount(\%hash)-1,"\n" } # "1: 2" sub foo { # +1 reference to %hash (compile time) $hash{x}++; print ++$hash{c},": ",refcount(\%hash)-1,"\n"; } BEGIN { print ++$hash{c},": ",refcount(\%hash)-1,"\n" } # "2: 3" sub bar { # +1 reference to %hash (compile time) $hash{y}++; my $hashref = \%hash; # +1 reference to %hash (runtime) print ++$hash{c},": ",refcount(\%hash)-1,"\n"; } BEGIN { print ++$hash{c},": ",refcount(\%hash)-1,"\n" } # "3: 4" # Remember "BEGIN" is compile time, the following is run time. # The three references to %hash are the lexical "my %hash", # as well as "sub foo" and "sub bar". print ++$hash{c},": ",refcount(\%hash)-1,"\n"; # "4: 3" } # Lexical scope of "my %hash" ends, -1 reference to %hash. # The following calls are in "eval" b/c otherwise the calls # would be another reference keeping &main::foo/bar alive. eval "foo()"; # "5: 2" eval "bar()"; # "6: 3" delete_sub 'bar'; # -1 reference to %hash eval "foo()"; # "7: 1" delete_sub 'foo'; # -1 reference to %hash (1-1=0) => "DESTROY" print "Done.\n";

        I get it!

        Perl uses a simple counting-stack mechanism for scope retention. Which explains, if mechanically inclined, your preference for "..so long as there is something that refers to the variable, Perl keeps it around..".

        My own memory faculty is far too degenerated to keep such a count and so opt instead to identify the "seed" (count-1) whose lifespan dictates over its dependents (ie FORCES accessibility/life). It wouldn't be the first time this largely 'hands off' approach has landed me in trouble and, if it is not too late, already have an immensely richer appreciation of the wider underlying schema.

      Yes, on a correct re-reading, as prompted by you, I got it that "inaccessible" just meant directly inaccessible by its lexical-name (aka symbol) from the outside, not that the state of the lexical was inaccessible. I tend to go for as high a level of logical conceptualisation as can be managed, where accessibility of the "state" would be what primarily matters and once that established then dig into the nuts and bolts of mechanisms for it. Just my peculiar (not very perlish?) orientation!