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

Greetings, honorable monks, I have two modules:
##### a.pm ##### package a; use b; sub import { print "import in $_[0] called from ". (caller(1))[3] ." \n"; } 1;
##### b.pm ##### package b; use a; sub import { print "import in $_[0] called from ". (caller(1))[3] ." \n"; } 1;
and a script:
### test.pl ### #!/usr/bin/perl use strict; use warnings; use a;
Running this script prints:
import in b called from a::BEGIN import in a called from main::BEGIN
Whereas I would expect there to be another "import in a called from b::BEGIN" at the beginning, from the "use a;" in b.pm. I know that each module is only compiled once, but import should be called each time, and there are three "use" statements, so shouldn't there be three imports?

Thanks in advance,
Aaron

Replies are listed 'Best First'.
Re: Circular use doesn't call import() as expected...?
by tilly (Archbishop) on Nov 04, 2005 at 03:33 UTC
    The short explanation is that Perl avoids endless loops by refusing to try to compile files that it is already compiling.

    Here is how that rule plays out in this case.

    1. Perl starts compiling test.pl.
    2. Perl sees that it needs to use a, and so starts compiling a.pm.
    3. Perl sees that it needs to use b, and so starts compiling b.pm.
    4. Perl sees that it needs to use a, but we're already compiling a.pm so Perl avoids an endless loop by not trying to compile the rest of it right now.
    5. Perl tries to run a->import, but there is no import defined for a, so Perl doesn't do anything. No output here, but you expected some.
    6. Perl finishes compiling b.pm and runs b.pm.
    7. Perl returns to a.pm and runs b->import, which runs. Output here.
    8. Perl finishes compiling a.pm and runs a.pm.
    9. Perl returns to test.pl and runs a->import, which runs. Output here.
    10. Perl finishes compiling test.pl and runs test.pl.

    To see that this is what is happening, change a.pm to the following:
    package a; sub import { print "import #1 in $_[0] called from ". (caller(1))[3] ." \n"; } use b; sub import { print "import #2 in $_[0] called from ". (caller(1))[3] ." \n"; }
    This will change the output to:
    import #1 in a called from b::BEGIN import in b called from a::BEGIN import #2 in a called from main::BEGIN
    and you can see that b.pm really is calling a->import, but Perl has only compiled the file up to the use statement.

    (General note. Circular dependencies tend to be poorly handled in many languages. If you can, you really want to avoid that. One strategy to handle the circularity is to explicitly require and import in the module that you want to see be loaded first. Another is to move critical code - such as imports - so they are executed before use statements.)

Re: Circular use doesn't call import() as expected...?
by ikegami (Patriarch) on Nov 04, 2005 at 05:27 UTC

    For starters, don't use 'b' for a module name. In Windows, it conflicts with the B:: hierarchy.

    Wrap the sub import in a BEGIN and move it above the use, as shown below. You're suffering the same problem as Undefined subroutine errors.

    ##### aa.pm ##### package aa; sub import { print "import in $_[0] called from ". (caller(1))[3] ." \n"; } use bb; # ... 1; ##### bb.pm ##### package bb; sub import { print "import in $_[0] called from ". (caller(1))[3] ." \n"; } use aa; # ... 1; ################# >perl -e "use aa; use bb;" import in aa called from bb::BEGIN import in bb called from aa::BEGIN import in aa called from main::BEGIN import in bb called from main::BEGIN

    Circular inclusions are almost always caused by bad design. The above is simply a workaround.

    Update: It turns out the BEGIN is not necessary, but the use must follow sub import.

Re: Circular use doesn't call import() as expected...?
by asokoloski (Sexton) on Nov 04, 2005 at 17:50 UTC
    Thanks everyone, it seems pretty obvious now that it's been explained to me :)

    I can't really avoid the circular use, it's in a pretty big production system. But now I found out I can fix the problem I was having by putting my "@EXPORT" declarations and such in a BEGIN block. Thanks!