http://www.perlmonks.org?node_id=93188

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

Hi, Monks ...

I recently asked for a review of my first module, and I received some very useful feedback. However, there is a question that I didn't ask that goes to the heart of my module's design, and perhaps future modules that I hope to write.

My module exports several functions; each function takes no arguments, and returns a scalar (or in one function, a three element array.) When I first started designing the module, my thought was to, instead of exporting the various functions, I would instead export a single hash, with the keys being the function names and the values being, obviously, the return of each function.

The reason I did not do that is because several of the functions may take several seconds to complete, and so the act of "use"ing the module in a script made the entire script take that longer amount of time to complete. (Note though, that if all of the functions are exported and used in the script, the script will take the same amount of time.)

Perhaps the answer to my question is "TMTOWTDI", but is there any kind of consensus to whether it is better to have a module export functions (even though the functions take no args) or variables?

thanks for all of the help I've gotten so far, and all of the help I'll be getting in the future! :-) -s-

  • Comment on should a module export FUNCTIONS or VARIABLES?

Replies are listed 'Best First'.
Re: should a module export FUNCTIONS or VARIABLES?
by andreychek (Parson) on Jul 02, 2001 at 19:02 UTC
    In almost all cases, I think exporting a function would be better then exporting a variable. Another term for exporting a variable is called a "global variable", and most programmers feel that they are not a clean way to do programming. There is nothing wrong with having a subroutine that returns a scaler. Don't get me wrong though, there are times when global variables are useful or necessary, and TMTOWTDI applies.

    As a matter of fact though, it is actually possible to avoid both, if you ever decide to get into Object Oriented programming (OOP). I know you're just getting started, so don't feel pushed. There are advantages and disadvantages to both, as various discussions around here lately have proven. But it is a given that overall, OOP is considered cleaner then standard procedural programming, and you don't have to bother with exporting anything.

    For more information on OOP, check out "perldoc perlboot"
    -Eric.

      thanks ... that helps. I wish I could get the OO concept; I've gone through the perl manpages; I've got every ORA perl book; I've got the Conway book. The only thing I don't have is understanding. ;-)

      I'll just keep working towards that goal of being just another perl hacker! thanks again. -s-
        I wish I could get the OO concept; I've gone through the perl manpages; I've got every ORA perl book; I've got the Conway book. The only thing I don't have is understanding. ;-)

        I know I'm in a minority here, but I usually have problems with Perl documentation (in whatever form) first time out. It is seldom self-contained (frequently referring to something else in Perl you don't know anything about).

        My advice on Perl OO (or Perl anything) is:

        • read the documentation
        • do the examples
        • do something original with a knowledgable person in the vicinity for questions
        • after a while, re-read the documentation; you'll get a deeper understanding of the topic, and because you've learned other Perl things since the first reading, you'll understand why you didn't get it the first time :)
Re: should a module export FUNCTIONS or VARIABLES?
by VSarkiss (Monsignor) on Jul 02, 2001 at 19:31 UTC

    Heh. I was the one that suggested you use a tied hash.

    You need to be careful about one point: "exporting" is not the same as "exposing an interface". In Perl has, "export" has a precise meaning (to introduce symbol table entries into the caller's main package), whereas an interface style is strictly a design decision. The two are related, but not the same thing.

    Let me elaborate. Say you choose to present a function-oriented interface. You can choose to export the functions (using the standard Exporter module as your example code is doing). That means when I use the module, I get those sub symbols entered in my main package, potentially clashing with my own variable names or those from other packages.

    I can keep the function oriented interface but not export any names by using an object-oriented interface. So, for example, instead of this:

    use AIX::Sysinfo; ... my $host = hostname(); # sub exported from module my $version = system_version(); # sub exported from module
    I could have this:
    Use AIX::Sysinfo; ... my $sys = AIX::Sysinfo->new(); my $host = $sys->hostname();
    The difference between the first and second versions (as far as exported symbols is concerned) is that in the first example I have no control over clashing names like hostname and system_version, whereas in the second, name clashes aren't possible (well, except for the module name itself...) You notice that $sys, my handle to the package, is a lexical now.

    The same consideration applies to an interface exposed through variables, in this case a hash. I can choose to export the hash from my code, polluting my caller's namespace, or I can allow the caller to choose names by tying. Here's the first way:

    use AIX::Sysinfo_var; # making this up my $hostname = $sysinfo_hash{hostname}; # making this up my $version = $sysinfo_hash{aix_version};
    And here's the second:
    use AIX::Sysinfo_tie; ... tie %sysinfo, 'AIX::Sysinfo'; my $hostname = $sysinfo{hostname};
    Again, the difference is that the first module introduced the %sysinfo_hash into the caller's namespace, but the second module introduced no new symbols. Nonetheless, both are exposing a variable-oriented interface.

    HTH

      more excellent guidance... I wish I had the skills to implement your suggestion (esp. regarding the "tie"); hopefully, I will soon!

      thanks, -s-
Re: should a module export FUNCTIONS or VARIABLES?
by davorg (Chancellor) on Jul 02, 2001 at 19:33 UTC

    I agree that exporting functions is generally to be prefered to exporting variables, but obviously this is heavily dependent on the circumstances.

    In your module, will the majority of users need all (or even most) of the functions each time they use it? And what determines the return values. It sounds like they are fixed for each program run, but need to be calculated once - is that right?

    If that's the case, then it really sounds like you'd be best advised to use an object (and Conway's book is the clearest intorduction that I know of to Perl OOP) but in the short term why not consider just exporting one function that returns the hash of values.

    use SomeModule; my %hash_of_stuff = get_hash_of_stuff;
    --
    <http://www.dave.org.uk>

    Perl Training in the UK <http://www.iterative-software.com>

      thanks ... I think that this is a really good suggestion. Until I can properly grok the OO concepts and syntax, I'll export a single function that returns a hash of all the other items. If the user wants all of the functions available, they can access them directly.

      thanks again! -s-
Re: should a module export FUNCTIONS or VARIABLES?
by bikeNomad (Priest) on Jul 02, 2001 at 20:44 UTC
    I'd prefer not exporting anything unless the caller asked (look at the Exporter manual page). Note that (as far as I know) there are no (or few) modules in the core distribution that automatically export variables. For example, if I use File::Find I get find() shoved into my namespace, but still have to access $File::Find::name to get the full name. Likewise for $Data::Dumper::Purity and so on.

    Remember that if they have to, your users can always use the fully-qualified name of your variable. But it's better to provide access to package data via a method, and even better to not export anything and use the OO interface.

    I often use modules and explicitly import nothing at all:

    use File::Find ();