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.
To explain the problem: assume I have a module Foo that contains a subroutine foo(), a module Bar that contains a subroutine bar(), foo() calls bar(), and bar() calls foo(). I can write a script that calls foo() and the code will compile and run if either Foo or Bar do not employ Exporter, but the code will fail at compile or runtime if both Foo and Bar employ Exporter. Code that demonstrates failure at runtime is attached. A sample run follows.
"Don't do circular dependencies" was the first thought that came to mind, but myself and others have encountered compelling use-cases -- DRY, debug dumping, testing, higher-order programming, etc..
My first work-around was to put all interdependent subroutines into one module. This became unwieldy as the number of inter-dependent subroutines grew.
My second work-around was to group subroutines by dependency level and to place each group into a module. This damages conceptual integrity, the accounting is tedious and error prone, and architectural changes that cause a subroutine to change levels can result in O(n**2) effort.
More recently, I have been experimenting with putting one or a few subroutines into a source file and assembling Perl modules and scripts via #include statements and the C preprocessor via a hacked Filter::cpp:
https://github.com/rurban/Filter/issues/15The alpha code is limited and brittle. I don't know if the idea, if taken further, would lead to a generally useful solution.
What is the "proper" way to avoid or solve the problem of subroutine circular dependencies between modules?
David2022-03-13 11:52:17 dpchrist@tinkywinky ~/samba/dpchrist/sandbox/perl/Exporter-circular-use
$ cat /etc/debian_version 9.132022-03-13 11:52:27 dpchrist@tinkywinky ~/samba/dpchrist/sandbox/perl/Exporter-circular-use
$ uname -aLinux tinkywinky 4.9.0-17-amd64 #1 SMP Debian 4.9.290-1 (2021-12-12) x86_64 GNU/Linux
2022-03-13 11:52:29 dpchrist@tinkywinky ~/samba/dpchrist/sandbox/perl/Exporter-circular-use
$ perl -v | head -n 2This is perl 5, version 24, subversion 1 (v5.24.1) built for x86_64-linux-gnu-thread-multi
2022-03-13 11:52:33 dpchrist@tinkywinky ~/samba/dpchrist/sandbox/perl/Exporter-circular-use
$ ./Exporter-circular-use.t ok 1 - foo00 ok 2 - foo01 ok 3 - foo10 Undefined subroutine &Bar11::foo called at Bar11.pm line 22. not ok 4 - foo11 # Failed test 'foo11' # at ./Exporter-circular-use.t line 24. # got: 'main=7 # foo=6 # bar=5 # ' # expected: 'main=7 # foo=6 # bar=5 # foo=4 # bar=3 # foo=2 # bar=1 # ' 1..4 # Looks like you failed 1 test of 4.2022-03-13 11:54:39 dpchrist@samba /var/local/samba/dpchrist/sandbox/perl/Exporter-circular-use
$ freebsd-version 12.3-RELEASE-p22022-03-13 11:54:43 dpchrist@samba /var/local/samba/dpchrist/sandbox/perl/Exporter-circular-use
$ uname -aFreeBSD samba.tracy.holgerdanske.com 12.3-RELEASE-p1 FreeBSD 12.3-RELEASE-p1 GENERIC amd64
2022-03-13 11:54:45 dpchrist@samba /var/local/samba/dpchrist/sandbox/perl/Exporter-circular-use
$ perl -v | head -n 2This is perl 5, version 32, subversion 1 (v5.32.1) built for amd64-freebsd-thread-multi
2022-03-13 11:54:50 dpchrist@samba /var/local/samba/dpchrist/sandbox/perl/Exporter-circular-use
$ ./Exporter-circular-use.t ok 1 - foo00 ok 2 - foo01 ok 3 - foo10 Undefined subroutine &Bar11::foo called at Bar11.pm line 22. not ok 4 - foo11 # Failed test 'foo11' # at ./Exporter-circular-use.t line 24. # got: 'main=7 # foo=6 # bar=5 # ' # expected: 'main=7 # foo=6 # bar=5 # foo=4 # bar=3 # foo=2 # bar=1 # ' 1..4 # Looks like you failed 1 test of 4.
Exporter-circular-use-20220313-113141.tar.gz
Description: application/gzip