in reply to Packages and modules

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

Replies are listed 'Best First'.
Re: Re: Packages and modules
by behroozi (Beadle) on Jan 09, 2003 at 20:27 UTC
    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.

        There is no "default" import behavior, so the import() example that I used earlier was as much as you needed. The only reason that it is called "import()" is because most people use it either directly or indirectly (via Exporter) for exporting symbols to the caller's namespace. If you want to define your own import() sub without losing the functionality of Exporter, look at the export_to_level() function of Exporter, which would work like this:
        package Foo; use base Exporter; @EXPORT_OK=qw($foo $bar $baz); sub import { $foo = $_[0]; Foo->export_to_level(1, @_); #export symbols to caller }

        No more wizardry needed than that. perldoc'ing Exporter will get you more information if you run into trouble.

        Update: I apologize for not making this clear in the original post, but the above code was not intended to set $foo to the first argument passed when you say "use Foo LIST". Since "use Foo LIST" is roughly equivalent to "BEGIN { require Foo; Foo->import(LIST); }", the first argument to import() is the package name, so $foo should be set to "Foo". Yes, this was intentional. If you instead want to get the first argument, set $foo to $_[1]. If you also want to use Exporter, it is not necessarily a good idea to use shift() to remove first argument, as others have suggested, because this will make bad things happen if you subsequently call "Foo->export_to_level(1, @_);"
Re: Re: Packages and modules
by roundboy (Sexton) on Jan 09, 2003 at 21:10 UTC

    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