Re: Exporter and subroutine circular dependencies between modules

[email protected] (Diab Jerius via module-authors) Sun, 13 Mar 2022 18:08:50 -0400
Newsgroups perl.module-authors
Organization Center for Astrophysics | Harvard & Smithsonian
Message-ID <[email protected]>
--------------wPlgQA5r9v0Y1Z68tF8LaDpk
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 8bit

On 3/13/22 16:13, David Christensen wrote:
> module-authors:
>
> I have been wrestling with the Exporter module and subroutine circular 
> dependencies between modules for a number of years.  I have yet to 
> find a satisfactory solution.
>
[...]
>
> What is the "proper" way to avoid or solve the problem of subroutine 
> circular dependencies between modules?

The issue in your case is in part[1] due to the fact that in Foo11.pm, 
this part is run during the execution phase:

    require Exporter;
    our @ISA    = qw( Exporter );
    our @EXPORT    = qw( foo )

while

    use Bar11;

is actually run earlier, during the compilation phase, so Bar11 can't 
get any symbols from Foo11 because it's not actually exporting them yet.

If you move import of modules with circular dependencies to runtime 
rather than compile time:


    $ diff Exporter-circular-use{.orig,.new}
    diff Exporter-circular-use.orig/Bar11.pm
    Exporter-circular-use.new/Bar11.pm
    13c13,14
    < use Foo11;
    ---
     > require Foo11;
     > Foo11->import;
    diff Exporter-circular-use.orig/Foo11.pm
    Exporter-circular-use.new/Foo11.pm
    13c13,14
    < use Bar11;
    ---
     > require Bar11;
     > Bar11->import;


    $ perl Exporter-circular-use.t
    ok 1 - foo00
    ok 2 - foo01
    ok 3 - foo10
    ok 4 - foo11
    1..4

  Or move the export completely into compile time:

    $ diff Exporter-circular-use{.orig,.new}
    diff Exporter-circular-use.orig/Bar11.pm
    Exporter-circular-use.new/Bar11.pm
    9,11c9,11
    < require Exporter;
    < our @ISA    = qw( Exporter );
    < our @EXPORT    = qw( bar );
    ---
     > use parent 'Exporter';
     > our @EXPORT;
     > BEGIN{ @EXPORT = qw( bar ); }
    diff Exporter-circular-use.orig/Foo11.pm
    Exporter-circular-use.new/Foo11.pm
    9,11c9,11
    < require Exporter;
    < our @ISA    = qw( Exporter );
    < our @EXPORT    = qw( foo );
    ---
     > use parent 'Exporter';
     > our @EXPORT;
     > BEGIN { @EXPORT = qw( foo ); }

it also will work;


[1] I'm not completely solid on symbol resolution time in Perl, so I'll 
prevaricate.


--------------wPlgQA5r9v0Y1Z68tF8LaDpk
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <div class="moz-cite-prefix">On 3/13/22 16:13, David Christensen
      wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:[email protected]">module-authors:
      <br>
      <br>
      I have been wrestling with the Exporter module and subroutine
      circular dependencies between modules for a number of years.  I
      have yet to find a satisfactory solution.
      <br>
      <br>
    </blockquote>
    [...]<br>
    <blockquote type="cite"
      cite="mid:[email protected]">
      <br>
      What is the "proper" way to avoid or solve the problem of
      subroutine circular dependencies between modules?
      <br>
    </blockquote>
    <p>The issue in your case is in part[1] due to the fact that in
      Foo11.pm, this part is run during the execution phase:<br>
    </p>
    <blockquote>
      <p>require Exporter;<br>
        our @ISA    = qw( Exporter );<br>
        our @EXPORT    = qw( foo )</p>
    </blockquote>
    <p>while<br>
    </p>
    <blockquote>
      <p>use Bar11;</p>
    </blockquote>
    <p>is actually run earlier, during the compilation phase, so Bar11
      can't get any symbols from Foo11 because it's not actually
      exporting them yet.<br>
    </p>
    <p>If you move import of modules with circular dependencies to
      runtime rather than compile time:</p>
    <br>
    <blockquote>$ diff Exporter-circular-use{.orig,.new}<br>
      diff Exporter-circular-use.orig/Bar11.pm
      Exporter-circular-use.new/Bar11.pm<br>
      13c13,14<br>
      &lt; use Foo11;<br>
      ---<br>
      &gt; require Foo11;<br>
      &gt; Foo11-&gt;import;<br>
      diff Exporter-circular-use.orig/Foo11.pm
      Exporter-circular-use.new/Foo11.pm<br>
      13c13,14<br>
      &lt; use Bar11;<br>
      ---<br>
      &gt; require Bar11;<br>
      &gt; Bar11-&gt;import;<br>
    </blockquote>
    <br>
    <blockquote>$ perl Exporter-circular-use.t <br>
      ok 1 - foo00<br>
      ok 2 - foo01<br>
      ok 3 - foo10<br>
      ok 4 - foo11<br>
      1..4<br>
    </blockquote>
     Or move the export completely into compile time:
    <blockquote>
      <p>$ diff Exporter-circular-use{.orig,.new}<br>
        diff Exporter-circular-use.orig/Bar11.pm
        Exporter-circular-use.new/Bar11.pm<br>
        9,11c9,11<br>
        &lt; require Exporter;<br>
        &lt; our @ISA    = qw( Exporter );<br>
        &lt; our @EXPORT    = qw( bar );<br>
        ---<br>
        &gt; use parent 'Exporter';<br>
        &gt; our @EXPORT;<br>
        &gt; BEGIN{ @EXPORT = qw( bar ); }<br>
        diff Exporter-circular-use.orig/Foo11.pm
        Exporter-circular-use.new/Foo11.pm<br>
        9,11c9,11<br>
        &lt; require Exporter;<br>
        &lt; our @ISA    = qw( Exporter );<br>
        &lt; our @EXPORT    = qw( foo );<br>
        ---<br>
        &gt; use parent 'Exporter';<br>
        &gt; our @EXPORT;<br>
        &gt; BEGIN { @EXPORT = qw( foo ); }</p>
    </blockquote>
    <p>it also will work;<br>
    </p>
    <br>
    <p>[1] I'm not completely solid on symbol resolution time in Perl,
      so I'll prevaricate.<br>
    </p>
    <p><br>
    </p>
  </body>
</html>

--------------wPlgQA5r9v0Y1Z68tF8LaDpk--