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

Is it possible for two modules to use each other?

I'm trying to do something like this:

#a.pl use bspace; use cspace; bgo(); cgo(); #bspace.pm package bspace; use Exporter; use cspace; @ISA='Exporter'; @EXPORT=qw( bgo bhelp ); sub bgo { chelp(); } sub bhelp { print "bhelp\n"; } 1; #cspace.pm package cspace; use Exporter; use bspace; @ISA='Exporter'; @EXPORT=qw( cgo chelp ); sub cgo { bhelp(); } sub chelp { print "chelp\n"; } 1;

Running a.pl results in the following output:

chelp Undefined subroutine &cspace::bhelp called at cspace.pm line 8.

Is there some trick to making this work as expected, or can you just not go in circles like this?

---
A fair fight is a sign of poor planning.

Replies are listed 'Best First'.
Re: Circular usage
by Old_Gray_Bear (Bishop) on Jan 20, 2004 at 20:26 UTC
    Yes it is possible, by either accident or poor design.

    You get some really frightening errors when you "use warnings" on them as well. Most of your subroutines and/or methods are redefined (an error under "use warnings", quietly ignored with out), but the modules do what you expect them to do (last sub-routine redefined wins).

    Uncoupling the mess was fun. (For some slightly tweaked value of fun....)

    ----
    I Go Back to Sleep, Now.

    OGB

Re: Circular usage
by Anonymous Monk on Jan 20, 2004 at 19:44 UTC
    Yes this is possible, people do it all the time, but it's very poor design. All you need to do is forward declare those subroutines (perldoc perlsub)
Re: Circular usage
by cees (Curate) on Jan 21, 2004 at 01:54 UTC

    Yes it is possible, but it is not recommended. I just ran into a problem with a module on CPAN that suffers from a circular dependency that crops up in the strangest ways. It has to do with the fact that it is importing functions in a circular dependancy, which by the way is exactly what you are suffering from. Here is an example:

    #!/usr/bin/perl use Crypt::Random; use Crypt::Random::Generator; my $gen = new Crypt::Random::Generator Strength => 0; print $gen->integer(Upper => 100), $/;

    The above will fail (if you are using version 1.13 of Crypt::Random) with the following error:

    Undefined subroutine &Crypt::Random::Generator::makerandom_itv called at lib/Crypt/Random/Generator.pm line 82.

    Which looks pretty similar to your problem!

    The solution is to fully qualify your functions instead of importing them (ie use cspace::chelp in your bspace module).

    What can make this really frustrating, is that the module may appear to work correctly in most instances, but if the modules get used in the wrong order, you hit a problem. Check out the following code snippet which also triggers the Crypt::Random circular reference bug.

    #!/usr/bin/perl use Tk; use Net::SFTP; my $sftp = Net::SFTP->new('localhost');

    Which spews out the following errors:

    "makerandom" is not exported by the Crypt::Random module "makerandom_itv" is not exported by the Crypt::Random module "makerandom_octet" is not exported by the Crypt::Random module Can't continue after import errors at /usr/local/share/perl/5.8.2/Cryp +t/Random/Generator.pm line 12 BEGIN failed--compilation aborted at /usr/local/share/perl/5.8.2/Crypt +/Random/Generator.pm line 12, <GEN0> line 1. Compilation failed in require at /usr/local/share/perl/5.8.2/Crypt/Ran +dom.pm line 18, <GEN0> line 1. BEGIN failed--compilation aborted at /usr/local/share/perl/5.8.2/Crypt +/Random.pm line 18, <GEN0> line 1. Compilation failed in require at ../Crypt-DH-0.03/lib//Crypt/DH.pm lin +e 6, <GEN0> line 1. BEGIN failed--compilation aborted at ../Crypt-DH-0.03/lib//Crypt/DH.pm + line 6, <GEN0> line 1. Compilation failed in require at ../Net-SSH-Perl-1.25/lib//Net/SSH/Per +l/Kex/DH1.pm line 13, <GEN0> line 1. BEGIN failed--compilation aborted at ../Net-SSH-Perl-1.25/lib//Net/SSH +/Perl/Kex/DH1.pm line 13, <GEN0> line 1. Compilation failed in require at ../Net-SSH-Perl-1.25/lib//Net/SSH/Per +l/Kex.pm line 6, <GEN0> line 1. BEGIN failed--compilation aborted at ../Net-SSH-Perl-1.25/lib//Net/SSH +/Perl/Kex.pm line 6, <GEN0> line 1. Compilation failed in require at ../Net-SSH-Perl-1.25/lib//Net/SSH/Per +l/SSH2.pm line 6, <GEN0> line 1. BEGIN failed--compilation aborted at ../Net-SSH-Perl-1.25/lib//Net/SSH +/Perl/SSH2.pm line 6, <GEN0> line 1. Compilation failed in require at ../Net-SSH-Perl-1.25/lib//Net/SSH/Per +l.pm line 51, <GEN0> line 1.

    Not very nice is it... So the moral of the story is, don't use circular references, and if you really have to, fully qualify your functions.

    - Cees

Re: Circular usage
by ysth (Canon) on Jan 21, 2004 at 10:00 UTC
    Yes, you can do this, but only carefully. You have to make sure that @ISA and @EXPORT are set by the time they are needed. Remember that a use statement temporarily stops compiling the current file while the used file is compiled, executed, and then import called. (If the module had previously been used or required, the compilation and execution is skipped.)

    In your case, this is what happens:

    a.pl line 1 compiled: "use bspace" bspace line 1 compiled: package bspace; bspace line 2 compiled: use Exporter; Exporter.pm compiled main code of Exporter.pm executed Exporter::->import called (uselessly) bspace line 3 compiled: use cspace; cspace line 1 compiled: package cspace; cspace line 2 compiled: use Exporter; Exporter::import called (uselessly) cspace line 3 compiled: use bspace; bspace::->import called (but doesn't yet exist since bspace::ISA is no +t yet set to inherit import() from Exporter, so bgo and bhelp are not + imported into cspace) cspace line 4 compiled but not executed: @ISA = 'Exporter' cspace line 5 compiled but not executed: @EXPORT = qw(cgo chelp); cspace line 6 compiled: sub cgo { bhelp(); } cspace line 7 compiled: sub chelp { print "chelp\n"; } cspace line 8 compiled but not executed: 1; cspace lines 4,5,8 executed cspace::->import called: cgo and chelp are exported into bspace bspace line 4 compiled but not executed: @ISA = 'Exporter' bspace line 5 compiled but not executed: @EXPORT = qw(bgo bhelp); bspace line 6 compiled: sub bgo { chelp(); } bspace line 7 compiled: sub bhelp { print "bhelp\n"; } bspace line 8 compiled but not executed: 1; bspace lines 4,5,8 executed bspace::->import called: bgo and bhelp are exported into main a.pl line 2 compiled: use cspace cspace::->import called: cgo and chelp are exported into main a.pl line 3 compiled but not executed: bgo() a.pl line 4 compiled but not executed: cgo() a.pl line 3 executed: bgo() (imported from bspace) bspace::bgo calls chelp() (imported from cspace) cspace::chelp prints "chelp\n" a.pl line 4 executed: cgo() (imported from cspace) cspace::cgo calls bhelp() which was not imported from bspace and so do +esn't exist.
    So the solution is to start your packages like:
    package bspace; use strict; use warnings; use Exporter (); # () avoids (harmless but) useless call to Exporter:: +import; BEGIN { our @ISA = 'Exporter'; our @EXPORT = qw(bgo bhelp); }
    and only after that use any modules that might do recursive use. The same technique is needed when putting packages in the same file that use each other, e.g.:
    # A.pm package A; use strict; use warnings; use Exporter (); BEGIN { our @ISA = 'Exporter'; our @EXPORT = 'foo' } sub foo { print "foo" } # and in the same file A.pm package A::B; use A; sub bar { foo() } # without the BEGIN block above, the use A won't hav +e imported foo into A::B
      Nowadays, it is recommended to import 'import' from Exporter instead of inheriting it, so:
      package bspace; use strict; use warnings; use Exporter 'import'; BEGIN { our @EXPORT = qw(bgo bhelp); }
Re: Circular usage
by poum (Novice) on Jan 21, 2004 at 17:16 UTC
    I've had the same problem. The problem is not using each module in the other one, but Exporter. Shortly, in cspace, call bgo with bspace::bgo() and in bspace cgo with cspace::cgo(). poum