
On 24-Sep-2004, Jan-Willem Maessen
An anecdotal note -
hbcc (the front end to the pH and Eager Haskell compilers, and also of GRIN) contained several mutually recursive modules both in the compiler and in the prelude.
One of the best things we ever did was get rid of the mutual recursion. The resulting refactoring helped us to group related pieces which had (for historical reasons) ended up scattered. It also cut our rebuild time dramatically, and let us do cross-module inlining and optimization safely.
In short, using mutual recursion was probably a bad idea, and we found we were better off without it.
Here's another anecdotal note. When refactoring the Cryptol compiler, in particular when abstracting some interfaces to use type classes, I found that (1) In the intermediate stages of the refactoring, I needed to use mutual recursion in quite a few different places. This was a pain because it required manually writing .hi-boot files. This was also problematic because my colleagues were reluctant to have our sources depend on a rather poorly supported feature of Haskell, and were also reluctant to have implementation-specific files like .hi-boot files in our CVS repository. As a result, I did not check in the intermediate stages of the refactoring into our CVS repository. These factors made the development process more difficult, made the code review process more difficult, and increased the likelihood of a CVS conflict. (2) Although most of the mutual recursion occurred only in the intermediate stages of the refactoring, some of the mutual recursion remained at the end of the refactoring, forcing two modules with only the smallest degree of coupling to be combined into a single module. The two modules in question were (a) the module which defines the calling interface between the Cryptol front-end and the various Cryptol back-ends, and (b) the module which defines the structure which records the settings of command-line options. Here (a) depends on (b) because one of the parameters which is passed to the back-ends is the command-line option settings, and (b) depends on (a) because one of the option settings is the currently selected back-end (represented using an existentially quantified typeclass-constrained type). So while I agree that unrestrained use of mutual recursion can lead to poor structuring, I think that in many cases mutual recursion _is_ useful, either as an intermediate stage during refactoring or as a final stage. A compiler warning about mutual recursion (with some way to suppress it in individual cases) might be useful, but a compiler error or the requirement to manually write separate .hi-boot files is not helpful, IMHO. -- Fergus J. Henderson | "I have always known that the pursuit Galois Connections, Inc. | of excellence is a lethal habit" Phone: +1 503 626 6616 | -- the last words of T. S. Garp.