Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Unclear about 'our'

by ibm1620 (Hermit)
on Dec 27, 2022 at 16:50 UTC ( [id://11149136]=perlquestion: print w/replies, xml ) Need Help??

ibm1620 has asked for the wisdom of the Perl Monks concerning the following question:

I'm unclear on the concept of 'our'. I have a module, package foo;, that several different programs use. I want foo to contain the definition of a constant scalar bar that programs using foo can reference.

foo.pm

package foo; our $bar = 42; 1;
pgm.pl
#!/usr/bin/env perl use 5.36; use foo; # 'our $bar'? 'our $foo::bar'? 'my $foo::bar'? say $bar;
I've tried several variations on declaring $bar in pgm.pl (including not declaring it at all) without successfully referencing the value that was set in package foo.

Could some kind monk straighten me out? Is this even what 'our' is meant for?

Replies are listed 'Best First'.
Re: Unclear about 'our'
by haukex (Archbishop) on Dec 27, 2022 at 17:02 UTC

    $foo::bar is a "package variable", sometimes referred to as a "global", although IMHO I prefer at least "package global", because the variable does belong to the package - see perlmod. our makes it so that the name $bar refers to $foo::bar in the lexical scope it is used. Outside of package foo;, you need to either refer to it by its full name $foo::bar, or you need to make a local alias to it, as for example is possible with Exporter; there are more ways to do it but these are the two I prefer depending on the situation. Here's an example for the latter:

    foo.pm

    package foo; use warnings; use strict; use Exporter 'import'; our @EXPORT_OK = qw/$bar/; our $bar = 42; 1;

    script.pl

    use warnings; use strict; use feature 'say'; use foo qw/$bar/; say $bar;
      In your rewrite, could you have written my $bar = 42; instead?

      In the modules and programs I've written, I typically declare a bunch of 'my' variables up top, outside of any blocks, which subsequent subroutines can freely access, so I'm still baffled by what 'our' buys me.

        In your rewrite, could you have written my $bar = 42; instead?

        No, because lexical variables declared with my are limited in visibility to their lexical scope, in this case that's the file foo.pm; they don't show up in the package symbol table either. "Package globals" are "global" in the sense that you can reach across package boundaries to access other package's package variables - in the example here, you're reaching from package main into package foo. IMHO package globals are more and more rare and should nowadays only be used in places where they make sense.

        Note that package variables can always be accessed by their fully qualified name, e.g. $foo::bar, while our creates a shorter alias to those package variables, and the visibility of that alias is the current lexical scope.

        package foo; sub x { our $bar; # $foo::bar $bar++; my $quz; # a lexical $quz, not accessible outside this sub! } sub z { our $bar; # the *same* $foo::bar $bar++; my $quz; # a *different* lexical $quz } # no $bar here! but: sub w { $foo::bar++; # still the same $foo::bar }
        In the modules and programs I've written, I typically declare a bunch of 'my' variables up top, outside of any blocks, which subsequent subroutines can freely access, so I'm still baffled by what 'our' buys me.

        For example, if one of those my variables in your module was a default for something in one of the functions, then a user of your module could reach into your package and change that default, lexicals wouldn't allow this. (For debugging it's possible to inspect lexicals elsewhere, but this is not for production use.) I've whipped up a quick example of what I mean below.

        One example might be that in Data::Dumper, one way to change its configuration options is via such package variables, as in $Data::Dumper::Useqq=1. However, it should be noted that this change is then global, so if you were to do it in a module, a user of your module might be surprised when their Data::Dumper changes behavior - the OO interface of the module is probably better in that case. Another thing is that only package variables, not lexicals, can be used in local to temporarily change their value in the current dynamic scope (the current call stack).

        package bar; our $DEFAULT_QUZ = "World"; sub hello { my $what = shift || $DEFAULT_QUZ; print "Hello, $what!\n"; } package main; bar::hello(); # Hello, World! bar::hello("Alice"); # Hello, Alice! $bar::DEFAULT_QUZ = "Bob"; bar::hello(); # Hello, Bob!
        'our' gets you a "package global", accessible outside the package via <sigil>PACKAGE_NAME::var_name, or a variable that can be "imported" into your program's namespace.

        Hopefully, this code will clarify:
        FOO.pm

        package FOO; use warnings; use strict; use Exporter 'import'; our @EXPORT_OK = qw/$bar $baz Baz_Get_Set/; our $bar = 42; # Package global, visible as $FOO:bar my $baz = 66; # Not visible outside FOO - needs accessor sub sub Baz_Get_Set{ defined $_[0] and $baz=$_[0]; return $baz; } 1;
        pm11149136.pl
        use warnings; use strict; use feature 'say'; use lib "."; use FOO qw/$bar $baz Baz_Get_Set/; say "\$bar (an imported 'package' variable)=",$bar; say "\$bar (accessed explicitly via package name \$FOO::bar)=",$FOO::b +ar; $bar="Set via caller"; say "\$bar (accessed explicitly via package name \$FOO::bar after sett +ing via imported var)=",$FOO::bar; say "\$baz (direct attempt to access package 'my` variable)=",$baz; # +This returns UNDEF say "BAZ via getter/setter=",Baz_Get_Set(); # Nothing passed - this is + a GET say "BAZ get after setting to 55=", Baz_Get_Set(55);
        OUTPUT:
        bash-5.1$ perl pm11149136.pl $bar (an imported 'package' variable)=42 $bar (accessed explicitly via package name $FOO::bar)=42 $bar (accessed explicitly via package name $FOO::bar after setting via + imported var)=Set via caller Use of uninitialized value $baz in say at pm11149136.pl line 11. $baz (direct attempt to access package 'my` variable)= BAZ via getter/setter=66 BAZ get after setting to 55=55
        </c>

                        "These opinions are my own, though for a small fee they be yours too."

        The reason our appears to have little advantage over my is that you are misusing my.

        In the modules and programs I've written, I typically declare a bunch of 'my' variables up top, outside of any blocks, which subsequent subroutines can freely access.....

        Although this 'works', it is considered poor practice. It defeats the advantage of using my with use strict. Lexical (my) variables should be declared in the smallest possible scope. Accidental usage elsewhere will be detected at compile time. It is often useful to declare the same name in different scopes. (They actually refer to different variables). Both structured design and the newer object-oriented design discourage using subroutines that share a common pool of variables. Subroutines that do are much harder to test and debug than those that do not.

        Bill

        No, because Exporter would not be able to see it.

Re: Unclear about 'our'
by LanX (Saint) on Dec 27, 2022 at 18:11 UTC
    > Is this even what our is meant for?

    to expand on HaukeX's explanantion:

    our is meant (was introduced) to be analogue to my in declaring a "simple" var/symbol, but this time a package variable of the current package, not one private to the scope.

    Both are lexically scoped ° and with "simple" I mean not fully qualified, hence $name vs $Package::name

    strict -ness requires variables to be declared or fully qualified.

    before our was introduced, the only way to use a simple package var with strict was the vars pragma, which led to messy scopes.

    use strict; use warnings; { our $name = 42; # alias to $main::name my $ref_name = \$main::name; # ref to $main::name warn "$main::name - $name - $$ref_name"; package OTHER; # --- still same scope warn "$main::name - $name - $$ref_name"; $name = 666; # w/o strict this would be $OT +HER::name warn "$main::name - $name - $$ref_name"; } # out of scope warn "$main::name"; # --- compiletime error! # warn "$name - $$ref_name"; # Variable "$name" is not imported at c:/tmp/pm/our_vs_my.pl line 22. # Global symbol "$name" requires explicit package name (did you forge +t to declare "my $name"?) at c:/tmp/pm/our_vs_my.pl line 22. # Global symbol "$ref_name" requires explicit package name (did you f +orget to declare "my $ref_name"?) at c:/tmp/pm/our_vs_my.pl line 22. our $name; # rebind alias for following s +cope warn $name;

    42 - 42 - 42 at c:/tmp/pm/our_vs_my.pl line 9. 42 - 42 - 42 at c:/tmp/pm/our_vs_my.pl line 14. 666 - 666 - 666 at c:/tmp/pm/our_vs_my.pl line 18. 666 at c:/tmp/pm/our_vs_my.pl line 22. 666 at c:/tmp/pm/our_vs_my.pl line 32.

    one difference to my is that our vars are not destroyed at the end of scope, the alias is just released.

    Cheers Rolf
    (addicted to the 𐍀𐌴𐍂𐌻 Programming Language :)
    Wikisyntax for the Monastery

    °) basically: available till the end of the surrounding block

    UPDATE

    expanded examples

    Update

    for a better understanding: Perl4 didn't have strict or my resp. private variables. Every simple var automatically belonged to the current package and one often needed local for dynamic scoping at run time. our filled the conceptual hole to have compile-time checking for package vars in a more stringent way than use vars could provide.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11149136]
Approved by marto
Front-paged by kcott
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (3)
As of 2024-04-26 00:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found