|Think about Loose Coupling|
Re^3: Serious Exporter Problemsby etcshadow (Priest)
|on Jul 10, 2005 at 16:19 UTC||Need Help??|
Well, as to the bit about the arguments... they've really not been about questions of circular dependancies, but rather about the specific question at hand (importing from a module that imports from you), and the necessity of setting up of your exports at compile-time. That's why I happened to have a fairly detailed example of this imediately at hand.
As to the outright condemnation of circular-uses, I have to respectfully disagree. In managing a complex project, it can become a fairly arbitrary line to draw between your modules. It forces you to structure your code-containers around your code's calling-graph, rather than around the functional nature of your code.
I spend most of my time writing code for healthcare, so here is a simplified example: I want to be able to group code for patients in a Patient module, and code for claims in a Claim module. There are perfectly reasonable cases which require the processing of a claim to have to access patient functions, and vice-versa. Patient's have claims, and the description of a patient is not complete without his/her claim-history. When displaying a claim, I need to access the patient's information, as it is a fundamentally important part of the claim. If I understand your "circular uses are bad, 'mkay" correctly, then I should not be allowed to organize my code this way.
Also, as to the comment that other languages do not allow circular dependancies, that's just not true (in my experience). Certainly *some* other languages will not have a means to allow for it... but *some* other languages do all sorts of crazy things and impose limitations that I don't ever want to deal with as a user of that language. Even a language which provides as little in the way of facilities to the programmer as C has a means for dealing with circularly dependant libraries. First, there is a separation between headers and code... that goes a long way, on its own. Second, a fairly simple method for preventing pre-compiler infinite-loops is employed:
So, C, and any language which inherits it's basic separation of header files and code files from C, along with a pre-compiler as capable as C's is set. It can handle circular dependancies.
I guess that gets somewhat to a larger point about this question (as well as some of the issues you raised with circular uses in perl): different languages do things differently with respect to this, and in many of them (all, maybe), there is a necessary discipline to build atop the language, itself, to protect yourself from problems. In perl, this discipline includes understanding the difference between BEGIN time and run-time, and using that understanding appropriately. Maybe perl requires a little bit too much in the way of requiring discipline from its programmers, in this regard... on that I won't comment. It certainly wouldn't be the first time perl's been accused of requiring self-discipline of its programmers, rather than enforcing discipline upon them.
However, I actually don't think that there is anything all that wrong with publishing some best practices as a way to encourage that self-discipline in people who haven't yet attained the understanding of why that self-discipline is necessary. In my mind, telling people to always put their @ISA and @EXPORT setup in a BEGIN block is kind of like telling people to use strict. Actually, a better comparison would be like telling people to NEVER put a "my" operator in a conditional. The reason why it's important to do this is tricky to divine on your own, and may not bite you imediately. Still, there's no harm in doing it, and it may (proably will) prevent errors, later.