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

I'm writing a module. In scripts that use the module I need to communicate some information from the script to the module.

At least for the time being my plan was to define a global variable in the main package in the script that uses the module, then read the value of that variable in the module, e.g.:

SCRIPT
------

$main::var = "value"; use MyModule;

MODULE
------

package MyModule; my $var = $main::var;

I tried that and was shocked to find that $main::var is undefined to the code in my module.

It was my understanding that there is a package, main (always referred to in the literature as "the main package" or "package main"), whose variables could be accessed from anywhere using the fully qualified syntax. That doesn't seem to be the case -- what's going on?

I'd also appreciate information about what is the usual or best way to get data to a module from the code using the module.

Thanks,
JMM

Replies are listed 'Best First'.
Re: Packages and modules
by Fletch (Bishop) on Jan 09, 2003 at 16:16 UTC

    You're referring to it correctly, but use happens at compile time and you're not changing $::var until runtime. You either need to wrap the assignment in your main program in a BEGIN block, or you need to come up with a better method of passing things to your module (consider an import() routine so you could say use MyModule 'value', or implement accessors of some form (MyModule::setVar( "value" ) for example).

    Consult perldoc -f use and perldoc perlmod for further enlightenment.

Re: Packages and modules
by davorg (Chancellor) on Jan 09, 2003 at 16:24 UTC

    I assume that the

    my $var = $main::var;

    line is outsode of any subroutine in your module. In that case the code is executed when the module is loaded. All "use" statements are executed at compile time - this is before $main::var has been given a value.

    The quick fix is to put the line into a BEGIN block.

    BEGIN { $main::var = "value"; }

    But you should probably look for a more elegant solution.

    --
    <http://www.dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

Re: Packages and modules
by gjb (Vicar) on Jan 09, 2003 at 16:20 UTC

    There's no good reason at all to pass information in the main code to modules using global variables; global variables are evil. This style of programming makes code reuse very hard, and that is after all one of the primary goals of modules in the first place.

    Can't you just pass the information via the parameter of the functions that are defined in the module? Passing a reference to a data structure should be no problem.

    If you're a little more specific as to what kind of data, you might get some desing tips from fellow Monks.

    Just my 2 cents, -gjb-

Re: Packages and modules
by bart (Canon) on Jan 10, 2003 at 03:38 UTC
    I'd refrain from using a variable in main. If you insist on using a variable, at least use one in the package of your module. For example, that's what Data::Dumper does: you can control its behaviour by setting $Data::Dumper::Indent, $Data::Dumper::Terse,...

    File::Find, as another example, makes the long path of the file available via $File::Find::name, and not via $main::name.

Re: Packages and modules
by OM_Zen (Scribe) on Jan 09, 2003 at 19:06 UTC
    Hi ,

    The $main::var seems not to be passed as the use is implemented only during the compile time , the $main::var has to be passed within a BEGIN to make it compile first and then the use module cause the $main::var="value" is set during run time as of now . Hence a BEGIN in the perl script while you set the value , to $main::var = "value" ,so that it is also compiled with use module .
Re: Packages and modules
by Anonymous Monk on Jan 09, 2003 at 19:19 UTC

    Thanks for the replies. Well that explains that. You can probably tell that I haven't programmed in Perl that much. This is my first module.

    My real question at this point is what is the usual or recommended way to accomplish this -- passing data to a module from the code that uses it.

    You've given me several suggestions -- import method, general purpose setter method, etc. What do you recommend? In this case the data is just a scalar, a string.

    If I do need to write my own import method, can you give me any pointers to some information that will help me get started? At this point I wouldn't really know where to begin.

    Thanks,
    JMM

      The answer to this really depends on what type of module you are writing. For object-oriented modules, passing information to the module usually happens when you first create the object:
      my $foo = new Foo("bar"); package Foo; sub new { my $class = shift; my $var = shift; bless \$var, $class; }

      Otherwise, it is usual to provide both an import() interface and a variable in the namespace of the module in case the value needs to be changed after the module is first included:
      ---------(In Foo.pm)------------------ package Foo; sub import { $Foo::var = shift; } ---------(In a different file)-------- use Foo qw(bar); # Sets $Foo::var to "bar" #... $Foo::var = "baz"; # Changes $Foo::var to "baz"
        ---------(In Foo.pm)------------------ package Foo; sub import { $Foo::var = shift; }
        No, that's wrong. import() is called as a class method, thus the first argument will be the package name as a string, in this case: "Foo". You have to skip it, first, for example by using shift(), before you can get at the user supplied arguments.

        One major difference between normal subs and class methods, apart from this extra first parameter (more trouble than much else, if you ask me), is that inheritance only works on methods, and not on plain subs. And the workings of Exporter are precisely based on this inheritance, where your package inherits the class method "import" from Exporter. Therefore, for Exporter to work, import() must be a method.

        The package is not OO at this time.

        If I provide an import interface like you describe wouldn't I usually also need to account for the default import behavior? Would it be possible (simply) to design my own import routine which would do whatever, then invoke the default import method to operate on the remaining "arguments"?

        I'd specifically prefer to set it up so that the value is set when the module is first invoked and can not be changed later.

      It's hard to know for certain what the right approach is without knowing more about what you're doing. However, based on the information you've given, it sounds like an import method would probably be overkill. If you have just a single value to pass in, the approach I'd take is probably to provide a package method to set the variable; this can return its value if you want the outside world to see it. Here's a trivial example module:

      package Foobar; my $var; # If called with no arguments, just returns the value. Otherwise sets +and returns. sub foovar { $var = shift if @_; return $var; } # This version won't allow the value to be set twice sub foovar_once { die "Attempt to foovar more than once\n" if @_ && defined($var); $var = shift if @_; return $var; } 1;
      And here's a test script:
      use Foobar; if (1) { Foobar::foovar(7); print "1. The value is now ", Foobar::foovar(), "\n"; Foobar::foovar(9); print "2. The value is now ", Foobar::foovar(), "\n"; } else { Foobar::foovar_once(7); print "1. The value is now ", Foobar::foovar_once(), "\n"; Foobar::foovar_once(9); print "2. The value is now ", Foobar::foovar_once(), "\n"; }

      Change the 1 to a 0 in the if test to see the other behavior.

      HTH,
      --roundboy

Re: Packages and modules
by Anonymous Monk on Jan 09, 2003 at 21:23 UTC

    I'm not sure what more I can really tell you about what I'm doing -- I just want to communicate some data (in this case just a scalar value, a string) to a module from the code using the module.

    That's kind of what I've been thinking -- that writing my own import routine would be overkill, but I wasn't sure if that is the traditional way to do what I'm trying to do.

    Thanks for the suggestion, I guess that approach will suffice.

    I'm a little surprised that people haven't been quicker to explain how they actually do this in their modules -- this must be a pretty common need, to pass an argument to a module, isn't it?

    JMM

      In general, I have two types of modules:
      • Modules which are really related collections of functions
      • Modules which implement classes for OO perl
      In the former case, the Modules themselves are "stateless" -- i.e. they have no variables of its own. All data is passed to the various functions as function arguments. If those functions call other functions, they pass what arguments they need.

      In the latter case, data is passed in to the modules as parameters to the objects (as someone else already mentioned), and thus the data can be shared among the functions without being explicitly passed.

      Module-level state is problematic -- if multiple subsystems of your code use the same module, they might bang into each other.

      This is the same problem in general with global variables -- they act against subsystem decoupling, which is a powerful technique for minimizing the complexity, and thus increasing the reliability and extensibility, of the systems we are building.

      This is not to say that module-level state should never be used, but you should think carefully about the extensibility ramifications if you do use it, and have a good reason to pay this cost.

      Sort of a long answer for saying "function paraments or object instance variables..." I hope this helps...

      --JAS
Re: Packages and modules
by Anonymous Monk on Jan 10, 2003 at 14:52 UTC

    JAS,

    Great post. At first I didn't think any of that was really relevent to my situation, but I started thinking about it and it didn't take long before I realized that it had everything to do with my module. I hadn't really considered the issues you're talking about, but after reading your post I have many new ideas.

    Before I didn't think there was any reason to make my module OO, but now I can see that it might be the best way. In it's current form, a collection of related functions, it's impossible for my module to be stateless because there is data that needs to be accessible to multiple functions and that data is currently stored in module level lexicals -- so I guess OO is really the ideal way to implement the module in order to bundle the related data and functionality together.

    I can clearly see, now, how implementing the module as a class will make it much more flexible.

    Thanks for making the post, that information has already elevated my concept of how to do this kind of programming in Perl by leaps and bounds and will prove invaluable.

    bart,

    Thanks for the suggestion, but I'm way beyond that point by now thanks to the excellent help everyone has given me in their posts here.

    P.S. I don't really think File::Find makes a very good example for implementation.

    Thanks,
    JMM