
The original Haskell design included interface files usually with names like Main.hi IIRC, Yale Haskell expected them to be hand-written while HBC and GHC machine-generated but allowed the careful user to write them by hand. Sometime around Haskell 1.4 or 98, they were dropped because Yale Haskell had died and there seemed little point in specifying the format of machine-generated files. (Hugs' support for modules was added long after the original Hugs design - it was hard getting it to do as much as it does. At one point, I almost had it supporting mutually recursive modules but I couldn't quite get all the dependencies in the typechecker under control so I settled for just supporting module namespaces and qualifiers.) It is _almost_ possible to generate .hiboot files automatically. If you see a data declaration in the .hs file, copy it to the .hiboot file, if you see a function prototype, copy it, etc. That is, you generate a .hiboot file by just deleting all function definitions. One of the tedious bits is that that you need to do a tricky least fixpoint calculation just to figure out what is exported by a set of mutually recursive modules if they each reexport their imports. Generating .hiboot files by just deleting function definitions fails if there is no prototype for an exported function. A crude approach is to assume the type (\forall a. a) for any function with no prototype but, although this is sound (I think), it will cause valid programs to be rejected. This problem could be overcome by specifying it as the correct behaviour :-) (Is it sound to use (\forall a. a) in the presence of higher-kinded types and all those other extensions?) Hmmm, maybe someone familiar with the Haskell-source libraries could quickly hack up a program to generate .hiboot files automatically and make it available - that would quickly find the issues (and also identify any weaknesses in ghc's error checking of .hiboot files :-). -- Alastair Reid
It's interesting how other languages [handle mutually recursive modules] problem. In Modula-3 it is solved by explicit module interfaces and partial revelation. One can define type T in module A and type T in module B very abstractly as pointers to something and the complete data structures (which may contain references to either interface) are usually revealed in the implementations of the modules. I'm curious how Oberon solves it, because it doesn't need explicit interfaces but derives them from the implementation files. Probably one can say that Oberon extracts something like a .hiboot file from every module file automatically. Why can't GHC and Hugs go this way?