[Haskell-cafe] mutually recursive modules

Fergus Henderson fjh007 at galois.com
Mon Sep 27 13:46:25 EDT 2004

On 24-Sep-2004, Jan-Willem Maessen <Janwillem.Maessen at Sun.COM> wrote:
> 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.

More information about the Haskell-Cafe mailing list