views:

87

answers:

3

What's the best way to handle modules that use each other?

Let's say I have a module which has functions for hashes:

# Really::Useful::Functions::On::Hash.pm

use base qw<Exporter>;
use strict;
use warnings;

use Really::Useful::Functions::On::List qw<transform_list>;

our @EXPORT_OK = qw<transform_hash transform_hash_as_list ...>;

#...
sub transform_hash { ... }

#...
sub transform_hash_as_list {
    return transform_list( %{ shift() } );
}
#...
1

And another module has been segmented out for lists:

# Really::Useful::Functions::On::List.pm

use base qw<Exporter>;
use strict;
use warnings;

use Really::Useful::Functions::On::Hash qw<transform_hash>;

our @EXPORT_OK = qw<transform_list some_func ...>;

#...
sub transform_list { ... }

#...
sub some_func { 
    my %params = transform_hash @_;
    #...
}

#...
1

Suppose that enough of these utility functions are handy enough that I'll want to use them in BEGIN statements and import functions to process parameter lists or configuration data.

I have been putting sub definitions into BEGIN blocks to make sure they are ready to use whenever somebody includes the module. But I have gotten into hairy race conditions where a definition is not completed in a BEGIN block.

I put evolving code idioms into modules so that I can reuse any idiom I find myself coding over and over again. For instance:

sub list_if { 
    my $condition = shift;
    return unless $condition;
    my $more_args = scalar @_;
    my $arg_list  = @_ > 1 ? \@_ : @_ ? shift : $condition;
    if (( reftype( $arg_list ) || '' ) eq 'ARRAY' ) { 
        return wantarray ? @$arg_list : $arg_list;
    }
    elsif ( $more_args ) { 
        return $arg_list;
    }
    return; 
}

captures two idioms that I'm kind of tired of typing:

@{ func_I_hope_returns_a_listref() || [] }

and

( $condition ? LIST : ())

The more I define functions in BEGIN blocks, the more likely I'll use these idiom bricks to express the logic the more likely that bricks are needed in BEGIN blocks.

Do people have standard ways of dealing with this sort of language-idiom-brick model?

I've been doing mostly Pure-Perl; will XS alleviate some of this?

+2  A: 

If two modules are interdependent then there is some problem with the design. If i were you i'd think about refactoring my modules.

redben
That's a possibility but these aren't really "design" elements, but more or less a collection of code idioms, which are themselves useful in other code idioms.
Axeman
+2  A: 

You can have two modules that each call into the other, because those calls are done at runtime and by then "everything" will have been loaded. However, (obviously) that means that you need to get to the runtime phase without blowing up. One common approach would be to have some other module, included earlier than either of these two, use them both. As a direct result of removing the direct use statements, you will lose those imports, but importing symbols from one module to another is not such a good idea anyway.

Looking at the code you've written, I'm pretty surprised that you've come up with so many "new" ways of processing data that you feel a need to pull these out into separate libraries. Have you looked at the standard libraries Hash::Util, List::Util, and List::MoreUtils? I would suggest you step back from your libraries and code in a more idiomatic Perlish way, using the standard libraries.

Ether
`List::Util` and `List::MoreUtils` definitely, but `Hash::Util` is pretty much useless as a general utility library for hashes, unless your concern is locking keys and a few other things that deal with the hard structure of hashes and not the general abstraction that they serve as the common implementation of: name-value associations.Ironically, I want to put a generalized hash utility out on CPAN and this is partly what motivates my concern about my "solution". AFAIK, imports are mainly a bad idea in *OO* modules when they give false positives about class capability and inferred behavior.
Axeman
+5  A: 

If you want to have mutually dependant modules, an easy way to make that work is to use late binding subroutine calls with fully qualified names (in other words, use parens on the sub call). Prototypes wont work, but perl wont care that Somepackage::mysub() isnt defined until you actually try to call it. When I am writing mutually dependant modules, I usually keep them in the same file which simplifies the situation by avoiding imports entirely.

Also, there is no need to define a sub in a BEGIN block since sub name {...} is the same as BEGIN {*name = sub {...}}

Eric Strom