Erik Bennett
Erik Bennett

Reputation: 1099

Can I declare globals in a subroutine in perl?

Can I declare globals in a subroutine in perl with use strict?

Consider the following code:

#!/usr/bin/perl
# $Id: foo,v 1.5 2019/02/21 10:41:08 bennett Exp bennett $

use strict;
use warnings;

initialize();

print "$lorem\n";

exit 0;

sub initialize {
    # How would one delcare "$lorem" here such that it is available as
    # a global?

    $lorem = <<_EOM_
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
_EOM_
    ;
}

Note that I'm not asking if (ab)using globals in this fashion is a good idea; I'm certain that it isn't.

I've tried a few combinations of our and $main::, but they all fail in just the manner you might expect them to.

At this point, I'm just curious. Can it be done? I wonder if some sort of shenanigans with the BEGIN block would work.

The following will work, but as @simbabque points out, it's ugly:

#!/usr/bin/perl
# $Id: foo,v 1.7 2019/02/21 19:48:26 bennett Exp bennett $

use strict;
use warnings;

initialize();

printf("$main::lorem\n");

exit 0;

sub initialize {
    # How would one delcare "$lorem" here such that is is available as
    # a global-ish?

    our $lorem = <<_EOM_
    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
_EOM_
    ;
}

Upvotes: 1

Views: 121

Answers (2)

simbabque
simbabque

Reputation: 54381

For your specific example, you don't need a global variable.

Perl has package variables. Those are created with our and can also be accessed with $namespace:: (where main is the default namespace, and $:: works for that as well). Those are global, but we rarely call them that.

You need to keep in mind that our makes a lexical alias, so if you declare it inside the sub, it will not be available outside, because there is no lexical alias in that larger scope.

use strict;

sub foo {
    our $bar = 123;
}

foo();
print $bar; # error

You need to declare the variable in a larger scope.

use strict;

our $bar;

sub foo {
    $bar = 123;
}

foo();
print $bar;

This will work, because $bar is now available in the file's scope.

All of this only applies when use strict is turned on. If you don't declare a variable, it will automatically become a package variable. However, if you turn on strict, you have to declare all variables. Therefore you need to be explicit.

You can also use my if you declare it outside of the sub.

use strict;

my $bar;

sub foo {
    $bar = 123;
}

foo();
print $bar;

Since you're doing this in a script and there is no explicit package declaration, I think it's safe to assume there are no other modules involved. In that case, it does not matter if you use my or our.

If you were using this in a package with different files involved it would make a difference. Variables declared on the file scope with my are kind of private, as there is no way to access them from the outside directly.

package Foo;
use strict;

my $bar = 123;

### other file
use Foo;

# no way to get $bar as there is no $Foo::bar

But if you use our (or the outdated use vars) it will become a package variable.

package Foo;
use strict;

our $bar = 123;

### other file

use Foo;
print $Foo::bar;

Can I declare globals in a subroutine in Perl?

Yes, you can declare package variables in a subroutine with our. But you can't access them as lexical variables outside the scope they've been declared in, so you need to access them with their fully qualified package name, and that is ugly.

Upvotes: 5

Erik Bennett
Erik Bennett

Reputation: 1099

I believe the answer is "no". Not with use strict. Among the features of use strict is checking whether or not you're using a variable before it's declared.

The notion of wanting to do just that, as I did above, is at odds with the compile time checks of use strict.

Thanks to @simbabque for helping me think through this more clearly.

Rather than remove the use strict, I'm going to move the ugliness of the huge $lorem type variables (and there's a lot of them) to a separate package.

-E

Upvotes: 0

Related Questions