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

I'm trying to write code to comply with Perl best practices, and I hear everywhere that global variables are evil and should not be used (and I'm even beginning to understand why!) However, I'm not sure what I should do in the following case.

I have a perl script that looks like this:

#!/usr/bin/perl use strict; require "lib.pl"; my $global_variable1='a'; my $global_variable2='b'; my $global_variable3='c'; switch(&subroutine1){ case 0: &subroutine2; case 1: &subroutine3; case 2: &subroutine4; }

The file lib.pl looks like this:

#!/usr/bin/perl use strict; sub subroutine1 { return rand(3); } sub subroutine2 { print $global_variable1; } sub subroutine3 { print $global_variable2; } sub subroutine4 { print $global_variable3; }

What this does, of course, is that Perl tells me that $global_variables 1-3 in the file lib.pl need an explicit package name and compilation fails.

What is the best practice way of doing this? The global variables in the original script are things like paths to various directories, database variables, etc. My question really is: How do I use globals without using globals?

Please explain things slowly, without using any big words, because I'm a Perl noob!

Replies are listed 'Best First'.
Re: What to use instead of global variables
by GrandFather (Saint) on Mar 28, 2012 at 06:21 UTC

    One of the programming virtues often talked of in the context of Perl in particular is laziness. The rational is that laziness means doing the least amount of work over the lifetime of a piece of code, not just "right now". In the case of globals that generally means don't use em because over the lifetime of a script they are likely to lose you more time than they save due to "action at a distance" bugs.

    However global variables are not really the issue in the sketch code you have shown. A bigger issue is using &subname to call subs instead of subname() - that really doesn't do what you think it does. Another issue is the suggestion of use of 'switch' which is not a Perl construct, but if you use 5.010 you can use given/when instead.

    But your real issue seems to be sharing variables outside their package. That is a special case of "global variables" and can be ok in some situations, but mostly isn't. However your code looks much more like it should make use of object oriented techniques. Ok, some big or unfamiliar words there I suspect, but OO needn't be that frightening. Consider:

    use strict; use warnings; use 5.010; package LibStuff; sub new { my ($class, %params) = @_; return bless \%params, $class; } sub picker { my ($self) = @_; my @keys = keys %$self; return $keys[rand @keys]; } sub getter { my ($self, $param) = @_; return "$self->{$param}"; } sub sub0 { my ($self) = @_; say $self->{0}; } sub sub1 { my ($self) = @_; say $self->{1}; } sub subWibble { my ($self) = @_; say $self->{wibble}; } package main; my $libObj = LibStuff->new(0 => 'a', 1 => 'b', wibble => 'c'); say $libObj->getter($libObj->picker()); given ($libObj->picker()) { when ('0') {$libObj->sub0()} when ('1') {$libObj->sub1()} when ('wibble') {$libObj->subWibble()} }

    The class is the name of the package: LibStuff. new is a constructor and makes a LibStuff object so $libObj gets assigned a LibStuff object (an "instance" of the LibStuff class).

    The other subs in the LibStuff package are members of LibStuff and the $self variable is the instance (the thing that got assigned to $libObj) the method is working with.

    The only really magic bit is bless which takes a reference to something (in this case a hash) and a name and creates a blessed reference (an object). Once you have an object you call methods on it using syntax like $libObj->picker().

    So what does that have to do with global variables? Well nothing really because you tend not to need them any more. The object packs everything it needs up inside itself and carries it around wherever it goes. The methods provide the usual ways of manipulating the contents of the object so it's much easier to ensure that the manipulation is correct and that removes a lot of the action at a distance issue that global variables engender. It also makes maintenance easier because the only code that fools around with the data is likely to be together in one place so again it's easier to ensure manipulation is correct.

    True laziness is hard work
Re: What to use instead of global variables (s/ / mal/)
by tye (Sage) on Mar 28, 2012 at 04:24 UTC
    I'm trying to write code to comply with Perl best practices
    [....]
    Please explain things slowly, without using any big words, because I'm a Perl noob!

    s t a r t
    o v e r

    It looks like an attempt to violate as many "best practices" as possible. So much so that it even makes my troll-sense tingle.

    - tye        

Re: What to use instead of global variables
by tobyink (Canon) on Mar 28, 2012 at 05:46 UTC
    #!/usr/bin/perl use feature 'switch'; use strict; require "lib.pl"; my $global_variable1='a'; my $global_variable2='b'; my $global_variable3='c'; given( subroutine1() ){ when (0) { subroutine2($global_variable1) } when (1) { subroutine2($global_variable2) } when (2) { subroutine2($global_variable3) } } #!/usr/bin/perl # lib.pl use strict; sub subroutine1 { return rand(3); } sub subroutine2 { print shift; }

    or, even better...

    #!/usr/bin/perl use strict; require MyLib; my @global_variable = qw( a b c ); subroutine2( $global_variable[subroutine1()] ); #!/usr/bin/perl # MyLib.pm use strict; sub subroutine1 { return rand(3); } sub subroutine2 { print shift; }
    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: What to use instead of global variables
by Cody Fendant (Hermit) on Mar 28, 2012 at 03:31 UTC
    What this does, of course, is that Perl tells me that $global_variables 1-3 in the file lib.pl need an explicit package name and compilation fails.

    It fails for me in a completely different way. Even if I use the Switch module. Which takes a scalar as an argument not a subroutine.

    Which version of perl are you using?

    But the answer to one of your questions is to use our instead of my.

Re: What to use instead of global variables
by nemesdani (Friar) on Mar 28, 2012 at 04:30 UTC
    Is there a switch-case struct in Perl? That's completely new to me.
      There's a module. But it doesn't work the way he thinks it does.

        There's a module. But it doesn't work the way he thinks it does.
        And which he shouldn't be using. The Switch module mistakenly escaped into Perl 5.8. Do not use it. The Switch module was deprecated in Perl 5.10 and removed in Perl 5.14.

        Notice that in Categorized Damian Modules, the Switch module is categorized by its author as "Damian modules you shouldn't use in production because their purpose is to explore and prototype future core language features".

        You can switch to the built-in given/when if using Perl 5.10+. If stuck with Perl 5.8, you could use an if/elsif block instead.

Re: What to use instead of global variables
by educated_foo (Vicar) on Mar 28, 2012 at 01:49 UTC
    I'm trying to write code to comply with Perl best practices...
    Please don't do this. As the quote at the top of the page says, "P is for Practical." Write your programs to solve your problems, not to please language scolds on the internet.

      While you have a point (do not follow style for style's sake), these "language scolds" are quite correct when the goal is to write maintainable code. (And it should be, for any non-small program!) If you think you will never have to revisit your code to fix or improve it (say, a one-time script), you are indeed free to ignore best practises and just write something that works.

      Thus spake the master programmer:
      "Though a program be but three lines long, someday it will have to be maintained."