tags:

views:

106

answers:

2

As long as I can remember, whenever I use a module I include a use line at the beginning of my code.

Recently I was writing two Moose object modules that use each other. Look at this over-simplistic example:

One module:

package M1 0.001;

use Moose;
use 5.010;
use namespace::autoclean;
# use M2; ### SEE QUESTION BELOW

has 'name' => (
 is       => 'ro',
 isa      => 'Str',
 required => 1,
);

has 'very_cool_name' => (
 is      => 'ro',
 lazy    => 1,
 builder => '_build_very_cool_name',
);

sub _build_very_cool_name {
 my ($self) = @_;
 my $m2 = M2->new( m1 => $self );
 return 'very ' . $m2->cool_name();
}

__PACKAGE__->meta->make_immutable;

1;

Another module: package M2 0.001;

use Moose;
use 5.010;
use Data::Dumper;    # TODO DEBUG REMOVE
use namespace::autoclean;
use M1;

has 'm1' => (
 is       => 'ro',
 isa      => 'M1',
 required => 1,
);

sub cool_name {
 my ($self) = @_;
 return 'cool ' . $self->m1->name();
}

__PACKAGE__->meta->make_immutable;

1;

And a short example that uses them:

use strict;
use warnings;
use 5.010;
use M1;
use M2;

my $m1 = M1->new(name => 'dave');
say $m1->very_cool_name();

Now, note the two modules use each other. M1 creates an instance of M2 and use it to generate very_cool_name, while M2 has an instance of M1 as an attribute.

Now, if I uncomment use M2; in M1 my eclipse goes mad. I guess it's because this a the loop created by this 'circular use`.

I commented this use and everything seems to works fine (I think...), but makes me really anxious (I'm using an object without use-ing its class! Is that 'legal'?..). This also which made me wonder:

  • When do I really need to use use? I think that I was taught to always use it, and surely when I use an object.

  • Is there anything fundamentally wrong when two modules use each other (in the sense that each uses an object of the other module; I know there some cases where this is logically impossible, but sometimes - as in this case - I think it does make sense).

+3  A: 

From a Perl perspective it's fine. use is smart enough to recognize that a module has already been loaded and not load it again. That said, the faint whiff of code smell should make you think about whether mutual dependency is acceptable or if you should refactor your code to eliminate it.

Eclipse, apparently, is not quite as bright. Commenting out one of the use statements to placate the editor will probably work but could create subtle bugs depending on the order the modules are loaded in (by the application) and when they use each others functionality. e.g. if M1 were loaded first, didn't use M2, and had a BEGIN block that needed functionality from M2... boom! If nothing happens until runtime (which is likely) you should be okay.

While it isn't an issue here, you'd also have problems if your use imported symbols from the other package. In that case couldn't remove the use without changing your code to fully qualify all references. (e.g. call MyModule::function() instead of just function())

Michael Carman
+2  A: 

There's no reason for M2 to use M1. You don't actually have a recursive dependency.

All M2 does is validate some object's classname -- which doesn't require having loaded M1 -- and call a method on it -- which means whoever constructed that object loaded M1.

Your rule about needing to use a class in order to call methods on an object of that class is wrong. use a class when you're going to call methods on the it directly -- for example, new. (This assumes pure OO modules, obviously, not things that export functions/symbols.)

Consider polymorphism. It is a feature that I can make my own subclass of M1 (called, say, M1A) and pass it to M2 without M2 having to know anything about the existence of M1A.

hdp
Say I'm retrieving a stored M1 object into `$m1`. As far as I understand from your answer, I do not need to `use M1`. So, will perl simply look in my `@INC` for `M1.pm` to see what it should do with this loaded object?
David B
*`use` a class when you're going to call methods on the it directly -- for example, `new`* -- note that `M1` calls `M2->new` without `use M2`. It seems to work, so what is the 'benefit' of adding a `use` here?
David B
@David: re your second question, if `M2` is already loaded, then `use M2` does nothing (assuming M2 exports nothing, which it shouldn't if it is an OO class). But *you must have `M2` already loaded* before you attempt to `M2->new`.
Ether
@Ether got it. Thanks (+1)!
David B
"retrieving a stored object" has nothing to do with what your classes should `use`. It's the responsibility of your deserialization layer to load whatever classes your data needs.
hdp