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.hsNoe 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 _ = 3As 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.