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

Hi all,

I've been coding Perl for 12 years. I wouldn't call myself a Perl coder, but I've written several large applications in the language. Last week, I heard, for the first time, that it's good practice to "use strict" and decided to try this out by bringing one of said applications into compliance. (Yeah, I'm Johnny-come-lately.)

Turned out to be not too bad, since my code was mostly fine in that regard (all variables within subroutines were declared with "my"), except for one small, but substantial problems. For ease of coding, I've separated subroutines into several files that I load into the main script using require. The main script is merely a collection of calls to the various subroutines. Now, I have some global variables that I define in the main script, and that are used throughout the sub-scripts. Now that I'm using the strict pragma, these variables are scoped to just the main script, and are empty in the subroutines.

Now the easiest solution would be to combine all of the files so as to eliminate the problem, but I feel like that can't (or at least shouldn't) be a "best practice". I guess I can define the variables within each of the files, but ditto. I've read a bunch of stuff regarding exporting variables, but I'm calling the other scripts using "require" not "use", and don't really understand why and if I should be using "use". I've also come across importing variables from a file that you're calling with require, but I have the opposite problem.

Am I missing something really obvious? I don't get it! Sorry about how muddled this question is, I'm just rather confused.

  • Comment on Using variables in require file... not possible?

Replies are listed 'Best First'.
Re: Using variables in require file... not possible?
by graff (Chancellor) on Oct 16, 2011 at 04:23 UTC
    Have you looked at using "our" instead of "my" to declare the variables that are used in both the main source code file and the various subroutine code files that it "require"s? That would allow you to keep the code separated into files as it is now: when variables are declared with "our" instead of "my", perl will treat them as having scope beyond any single source code file.

    Of course, that's a procedural short-cut -- a relatively messy expedient. A theoretically cleaner, more maintainable solution is that the various subroutines stored in separate files would be modified not to use global variables. Instead, to get a fully satisfying solution, you would do one of the following:

    • if the subroutines are using (and modifying) global variables "owned by" (first declared in) the main code file, then each of the subs (and sub calls) would be modified so that those globals are passed (by reference) as parameters to the subroutines, or
    • if the subs are "creating" global variables that are subsequently used in the main code (a particularly nasty habit, IMHO), they would be organized into packages (typically one package per source file, but you can choose to have one source file with multiple package ... declarations) and would be "objectified" so that the variables they create are accessible via objects that are given to the caller via a "new()" function (which is surprisingly simple to do), or
    • some combination of the above, depending on what your current "require"d files are doing.

    If that raises any procedural questions for you, please post some relevant code to demonstrate the issue.

    (P.S. (update): Welcome to the Monastery! I could tell right away that you're a new monk, because if you'd been around longer, you would have heard about "use strict" earlier...)

Re: Using variables in require file... not possible?
by eyepopslikeamosquito (Archbishop) on Oct 16, 2011 at 06:53 UTC

    I've separated subroutines into several files that I load into the main script using require
    That sounds like Perl 4-style, around 20 years behind the curve. Place your subroutines into one or more modules. Make each module testable in isolation. To put that to the proof, write some unit tests for each module. See perlmod and Test::More.

    I have some global variables that I define in the main script, and that are used throughout the sub-scripts
    Global variables are evil. Ruthlessly eliminate them. Clearly define the inputs and outputs of each of your subroutines. With that done, eliminating the nasty globals should be a doddle.

    I heard, for the first time, that it's good practice to "use strict"
    In addition to "use strict", add "use warnings".

Re: Using variables in require file... not possible?
by Rubber Cthulhu (Acolyte) on Oct 16, 2011 at 08:56 UTC
    Using global variables is really a bad practice. But if you realy want to make the program with this aproach, you should know some tricks. When you are using 'use strict' pragma you must declare your global variables using explicit package prefix. Though you don't declare any package in your main module, it has name 'main' defined implicitly. Hence you need to declare your 'foo' global as
    use strict; $main::foo = 'bar'; # that's ok
    instead
    use strict; $foo = 'bar'; # error!!! it doesn't work with strict pragma!
    So when you want to use global $foo (defined in main script) in other modules you need to use it's full name - $main::foo. Simple example:

    1. Script main.pl
    #!/usr/bin/perl use strict; use pkg1; $main::foo = 'bar'; print pkg1_get_foo()."\n";
    2. Module pkg1.pm
    use strict; sub pkg1_get_foo { return "From pkg1: $main::foo"; } 1;
Re: Using variables in require file... not possible?
by Marshall (Canon) on Oct 15, 2011 at 18:08 UTC
    use strict; is a good idea, but that is a different thing that "use ModuleX;

    "use ModuleX;" is a compile thing. And Module X will be compiled. require is a run time time thing - as far as I know.

    Most of my modules (but not all) are function oriented as opposed to OO. These modules export subroutine names that can be used by the "caller", the Perl code that "uses" the module can access these symbols. I can export function names or actually even "our" variables and I can export names either by default or by request.

    If I have an procedure oriented module, your interface to is is only via the symbols that I chose to export or allow you the import optionally. Basically exporting those symbols means that these name go into the package name space.

    An OO module is different in that not every method is necessarily in a symbol in the package table and there is more overhead to call a method in an OO package.

    When I "use ModuleX", it gets complied and executed. Require means that it must be there, but no code within it will be compiled and run until it is needed in the code.

    Normally you should "use" modules that you write yourself. "require" is actually a kind of "rare duck" and is used in special circumstances.

Re: Using variables in require file... not possible?
by choroba (Cardinal) on Oct 15, 2011 at 19:43 UTC
    Using global variables is considered bad practice (because they might trigger an action at a distance). That's why use strict makes it harder to use them. The proper way is to supply parameters to functions so it is known what data is accessed by which sub.
Re: Using variables in require file... not possible?
by NetWallah (Canon) on Oct 15, 2011 at 18:28 UTC
    If I understand your question correctly, youare tryg to send data from the main program(file) to the module. I recently did that, and here is the code I used (names modified to protect the innocent):
    MAIN code
    my $ProgramDir; BEGIN{ ($ProgramDir) = ( $0=~m|(.+)\/.+$|, $0=~m|(.+)\\.+$| ,"."); } use lib ( $ProgramDir, ); # Current Program's Dir use MyModule ("$ProgramDir/MyModule.db3");
    Inside the Module:
    !/usr/bin/perl -w use strict; use warnings; use Class::DBI; package MyModule; our $DBPath="MyModule.db3"; # Default file nme sub import{ my ($class,$newpath)=@_; $DBPath = $newpath || $DBPath; ## print "IMPORT: Setting path to '$DBPath' \n\t \n"; ## Delay this setting till AFTER IMPORT !! MyModule::DBI->connection(qq|dbi:SQLite:dbname=$DBPath|, "", ""); }
    "import" gets called automaticall, on "use".

                "XML is like violence: if it doesn't solve your problem, use more."