Haskell All-In-One

Haskell All-In-One is a Haskell utility which will take a program implemented in multiple modules and convert it to a single module.

It implements all of Haskell 98 (sorry, your -fglasgow-exts programs won't parse with the Language.Haskell.Parser parser so we can't deal with them, yet) as well as the hierarchical libraries. It can also correctly deal with literate Haskell scripts (provided you have GHCs unlit software available) and programs which need to be run through the c-pre-processor.

If you need --cpp, you need to either have the environment-variable CPP set to the executable, or have cpp in your path, or specify it with --cpp-program=.

If you need to process LHS files, you need to either have the environment-variable UNLIT set to the executable, or have unlit in your path, or specify it with --unlit-program=.

The program expects to be able to find interface files to the built in libraries. The distribution comes with three of these: haskell98.iface (the Haskell 98 language definition libraries), hierlibs.iface (the standard hierarchical libraries, from GHC 6.0) and ioexts.iface (which contains the definitions from the IOExts module). If you need to build .iface files yourself, a Perl script (buildExportFile.pl) is included for this purpose. It accepts .hi files as arguments and writes the interface file to stdout. By default, Haskell All-In-One opens the file hierlibs.iface in the CWD as the infrace; you can use -f or --interface= to specify their locations.

Finally, if the module you're All-In-One-ifying isn't a terminal module (i.e., a Main module) itself, you can build a wrapper file for the generated module so you can use it as before. Use -w or --wrap= to generate this.

THE WRAPPING FEATURE HAS BEEN TEMPORARILY REMOVED AS IT DOESN'T DEAL WELL WITH CLASSES, ETC. UNTIL A BETTER WAY TO HANDLE THIS CAN BE FOUND, YOU HAVE TO WRAP YOURSELF.

For example, suppose we have the following modules:

Foo.hs
module Foo where
import Bar
foo = bar (baz 'x')
Bar.hs
module Bar where
import Baz
bar x = x+1
Baz.hs
module Baz where
baz 'x' = 1
baz 'y' = 2
baz _   = 3

We can now run:

% ./singleModule --interface=haskell98.iface -i. Foo
Reading  Foo              ( ./Foo.hs )
Reading  Bar              ( ./Bar.hs )
Reading  Baz              ( ./Baz.hs )
Writing output to AllInOne_Foo.hs
Noe that we could have specified the output with -o; by default it goes to AllInOne_ followed by the original file name. We can look at the (ugly!) generated file:
% cat AllInOne_Foo.hs
module AllInOne_Foo where
import qualified Prelude
q_Foo_foo = q_Bar_bar (q_Baz_baz 'x')
q_Bar_bar q_Bar_x = q_Bar_x Prelude.+ 1
q_Baz_baz 'x' = 1
q_Baz_baz 'y' = 2
q_Baz_baz _ = 3
As you can see, each function f from module Mod is given the name q_Mod_f. Datatypes, etc., are given the name Q_Mod_F, to follow the capitalization rules of Haskell.

Big News Flash! I have successfully All-In-One-ified NHC to 44k lines of code in a single module. I have successfully compiled this with GHC and we can compare the relative speeds of normal NHC with NHC after All-In-One-ification. The compilation task for both NHCs was to recompile itself as one module :). The results of time are:

Normal NHC:
real    3m14.295s
user    3m9.879s
sys     0m2.143s

All-In-One NHC:
real    1m11.380s
user    0m10.102s
sys     0m1.711s

The distribution is available here:
Source plus interfaces: HAllInOne_src.tar.gz (this includes the necessary GMap libraries from Strafunski)
Linux x86 executable and interfaces: HAllInOne_linux.tar.gz
Sparc solaris executable and interfaces: HAllInOne_sparc_solaris.tar.gz
Windows executable and interfaces: HAllInOne_windows.tar.gz

There are two major known bugs:

  - Does not correctly handle:
      module Foo where
        import qualified Maybe as M
        import qualified List as M
        foo = M.isJust
    It incorrectly only will treat the last import as as the defining
    one.  I have never written code like this and personally find it
    rather ugly.  However, if this bites anyone, let me know and I'll
    move it up on the to-do list.

    (#106)

  - Recursive modules no longer loop.  I'm not sure they work
    completely, but seem to for simple test cases.

comments, corrections? email