question about "package" and variable scope...

R

Raymundo

Hi all,

I wrote a file that contains two packages:

----------------------------
#!/usr/bin/perl
use strict;
use warnings;

# "main" package, implicitly
sub foo {
print "sub foo in main\n";
}
our $foo = "scalar foo in main";

foo(); # no problem
print "$foo\n"; # no problem

# "Test" package begins here
package Test;

foo(); # This is an error, because this calls
"&Test::foo()" that doesn't exist
----------------------------

The last "foo();" results in an error, as you know, because "&foo"
exists in "main", not in "Test". It should be "main::foo();".


Ok, then... here is my question...

----------------------------
# (omit the same code as above)

# "Test" package begins here
package Test;

print "$foo\n"; # I think this is an error, too.
----------------------------

Execution result:

sub foo in main
scalar foo in main
scalar foo in main -- $main::foo is printed...


I thought that the last line would result in an error (just like the
above case).
And I thought it would, if "use strict" is ignored in the scope of
"Test", print null string (the value of $Test::foo).
But the last line printed the value of "$main::foo" variable...

I can't understand this.. Any help would be apprecieated.
 
J

Joost Diepenmaat

Raymundo said:
our $foo = "scalar foo in main";
package Test;

print "$foo\n"; # I think this is an error, too.

It isn't an error, it's just confusing. There are two things going on:

* Scoping.

our() has lexical scope, so as far as scope is concerned, it doesn't
care about packages at all. It works basically the same as:

my $foo = "lexical foo"; # lexicals are NOT bound to any package
package Test;
print "$foo\n";

* Association between the variable name and the package variable.

As I understand it, our() works as if it creates a new lexical
variable that is aliased to a package variable of the same name in the
current package. That is: the current package *at the our()
statement*, NOT the current package at whatever time you use the
variable.

See also perldoc -f our:

''An "our" declaration declares a global variable that will be visible
across its entire lexical scope, even across package boundaries. The
package in which the variable is entered is determined at the point of
the declaration, not at the point of use.''
 
S

Sherm Pendley

Raymundo said:
I can't understand this.. Any help would be apprecieated.

That's just "our" doing its job as designed. Your code is basically
the same as the example given in "perldoc -f our":

An "our" declaration declares a global variable that will be
visible across its entire lexical scope, even across package
boundaries. The package in which the variable is entered is
determined at the point of the declaration, not at the point of
use. This means the following behavior holds:

package Foo;
our $bar; # declares $Foo::bar for rest of lexical scope
$bar = 20;

package Bar;
print $bar; # prints 20, as it refers to $Foo::bar

sherm--
 
R

Raymundo

Oh my God... I misunderstood "our" completely...;;;

Thank you very much.

So, both "our" and "my" have lexical scope... Then, if I want to
declare a variable that is visible in "only this package"... is there
any way to do so? (other than using braces that enclose the entire
package)
 
J

Joost Diepenmaat

Raymundo said:
Oh my God... I misunderstood "our" completely...;;;

Thank you very much.

So, both "our" and "my" have lexical scope... Then, if I want to
declare a variable that is visible in "only this package"... is there
any way to do so? (other than using braces that enclose the entire
package)

You can use vars (which is also useful in some other situations):

#!/usr/bin/perl
use strict;
use warnings;

use vars qw($foo);

$foo = "scalar foo in main";

package Test;

print "$foo\n"; # error: Global symbol "$foo" requires explicit package name
 
R

Raymundo

You can use vars (which is also useful in some other situations):

#!/usr/bin/perl
use strict;
use warnings;

use vars qw($foo);

$foo = "scalar foo in main";

package Test;

print "$foo\n"; # error: Global symbol "$foo" requires explicit package name


Ooops.. I couldn't even imagine that "our" and "use vars" have
different scope rule...

Thanks again Joost for your helpful post.
 
T

Tad J McClellan

Raymundo said:
Ooops.. I couldn't even imagine that "our" and "use vars" have
different scope rule...


Simply read their documentation...

perldoc -f our

... within the lexical scope of the C<our> declaration ...

perldoc vars

the "use vars" and "use subs" declarations are not BLOCK−scoped.
They are thus effective for the entire file in which they appear...
 
H

Hans Mulder

Raymundo said:
Then, if I want to
declare a variable that is visible in "only this package"... is there
any way to do so? (other than using braces that enclose the entire
package)

Package variables are global in scope: you can access them from anywhere
using the $package::name notation.
(other than using braces that enclose the entire package)

Even that won't help:

$foo = "foo from main\n";

{
package Foo;

$foo = "foo from Foo\n";
}

print $foo; # foo from main
print $Foo::foo; # foo from Foo

You can use the $Foo::foo notation to access variables in packages
that aren't mentioned anywhere else in your script. Perl will
silently create the package if that's what it takes to make this work.

If you want to restrict visibility, you'll have to use "my". But "my"
pays attention only to braces and file boundaries, not to packages.

Hope this helps,

-- HansM
 
M

Martin Kißner

Sherm Pendley wrote :
That's just "our" doing its job as designed. Your code is basically
the same as the example given in "perldoc -f our":

An "our" declaration declares a global variable that will be
visible across its entire lexical scope, even across package
boundaries. The package in which the variable is entered is
determined at the point of the declaration, not at the point of
use. This means the following behavior holds:

package Foo;
our $bar; # declares $Foo::bar for rest of lexical scope
$bar = 20;

package Bar;
print $bar; # prints 20, as it refers to $Foo::bar
This does not work("print $bar" will not print anything in this case),
if Bar is in a separate file Bar.pm and included with 'use Bar;

Why?
How can I use variables across Packages wich are in separate files?

Thanks in advance
regards
Martin
 
S

Sherm Pendley

Martin Kißner said:
Sherm Pendley wrote :

This does not work("print $bar" will not print anything in this case),
if Bar is in a separate file Bar.pm and included with 'use Bar;

Why?

Our declares variables as being global within a lexical context; a
different file is in a different lexical context.
How can I use variables across Packages wich are in separate files?

Use the full package name - i.e. "print $Foo::bar;"

Or, have a look at 'perldoc Exporter' if you'd like to make the
"short" form available to code that uses your module.

sherm--
 
B

Ben Morrow

Quoth Martin =?UTF-8?Q?Ki=C3=9Fner?= said:
Sherm Pendley wrote :
This does not work("print $bar" will not print anything in this case),
if Bar is in a separate file Bar.pm and included with 'use Bar;

No. Seperate files have separate lexical scopes: 'my' variables are
never visible across files, and 'our' variables must be referred to by
their full package name.
Why?
How can I use variables across Packages wich are in separate files?

The first question is: are you sure you need to? It's often a bad idea
to do this; it may be better to provide a function or OO interface to
the variable.

That said, you have three options:

1. Refer to the variable by its fully-qualified name, $Foo::bar.

2. Export the variable into the 'Bar' namespace with Exporter,
something like this:

package Foo;

use base qw/Exporter/;
our @EXPORT_OK = qw/$bar/;

1;

package Bar;

use Foo qw/$bar/;

1;

3. Make a new 'our' variable in the second file that refers to
$Foo::bar:

package Bar;

#...

package Foo;
our $bar;
package Bar;

#...

1;

Ben
 
M

Martin Kißner

Sherm Pendley wrote :
Our declares variables as being global within a lexical context; a
different file is in a different lexical context.

Thank you. I wasn't aware of this.
Use the full package name - i.e. "print $Foo::bar;"

Or, have a look at 'perldoc Exporter' if you'd like to make the
"short" form available to code that uses your module.

'perldoc Exporter' explains how to use Exporter in modules (I didn't
read it thoroughly yet).
Is it also possible to use it in the main script to make variables
declared in the main script accessible inside the module?

I know I can use '$main::variablename' but I am just interested if I
could could also use Exporter for this.

regards
Martin
 
M

Martin Kißner

Tad J McClellan wrote :
Simply read their documentation...

perldoc -f our

... within the lexical scope of the C<our> declaration ...

I wasn't aware that the lexical scope is restricted to the file.
Thanks to sherm I am now.

regards
Martin
 
S

Sherm Pendley

Martin Kißner said:
'perldoc Exporter' explains how to use Exporter in modules (I didn't
read it thoroughly yet).
Is it also possible to use it in the main script to make variables
declared in the main script accessible inside the module?

Not that I know of, and for good reason. Modules should be self-
contained, with as little knowledge as possible of anything outside of
their own package. If the main program needs to provide some variable
to the module, it should either set a variable in the module's
package, or pass it to a function (or method) that the module
supplies.

For example, when you're using the CGI.pm module, you use a variable
in the module's name space ($CGI::pOST_MAX) to set the maximum amount
of data that will be accepted from a form.

sherm--
 
M

Martin Kißner

Sherm Pendley wrote :
[...] If the main program needs to provide some variable
to the module, it should either set a variable in the module's
package, or pass it to a function (or method) that the module
supplies.

This is how I do it right now.
Actually I am talking about a script and a couple of selfmade modules
which I use to build a multilingual website with a MySQL database in the
background. I also use HTML::Template.

My modules need access to the server variables and some other global
information.
For the sake of shortness and readability I declared some variables in
the main script for this purpose like:

my $language = de; # or en or nl ...
my $uri = $ENV{REQUEST_URI};
my $docroot = $ENV{DOCUMENT_ROOT};
....

Since these variables never change during one run of the script I
thought there must be an easier way to pass them to functions/methods
inside the modules than always passing them with every call of one of
those functions/methods. $main::uri will do but there might be another
option.
For example, when you're using the CGI.pm module, you use a variable
in the module's name space ($CGI::pOST_MAX) to set the maximum amount
of data that will be accepted from a form.

I didn't consider this possibility until now. This might be helpful in
other situations but if I load different modules depending on conditions
I think this might not be very comfortable.

Thank you for your help
regards
Martin
 
J

John Bokma

Martin =?UTF-8?Q?Ki=C3=9Fner?= said:
my $language = de; # or en or nl ...
my $uri = $ENV{REQUEST_URI};
my $docroot = $ENV{DOCUMENT_ROOT};

my $settings = {

language => ...
uri => ...
docroot => ...
};

You might want to create a module Settings with a get class method that
just returns that.
 
H

Hans Mulder

Martin said:
$main::uri will do but there might be another option.

You could spell $main::uri as $::uri

That way, you can still tell at a glance which variables are
global, and this notation is hardly longer than normal variables.

Hope this helps,

-- HansM
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,528
Members
45,000
Latest member
MurrayKeync

Latest Threads

Top