
There is another solution to the problem of configurational parameters. The main part of the solution is portable, does not depend on any pragmas, does not use unsafe operations, does not use implicit parameters, and does not require any modifications to the user code. I must warn that it is also potentially vomit-inducing. It seems that the problem at hand naturally splits into two phases: building the configuration environment, and executing some code in that environment. The phases are executed sequentially. The facts suggest the use of a SupedMonad. SuperMonad is very well known and often used, even by people who never heard of simpler monads. The following code is an illustration. Suppose file '/tmp/a.hs' contains the following user code, which is to run within the configuration environment provided by the module Config. For simplicity, our configuration is made of one Int datum, config_item:
File "/tmp/a.hs"
import Config (config_item)
foo = "foo shows: " ++ (show config_item)
bar = "bar shows: " ++ (show config_item)
main = do print foo print bar print foo
We specifically illustrate the reading of the config item several times. The following code runs the first phase: reads the configuration, build the SuperMonad and runs the SuperMonad.
import System (system, ExitCode(ExitSuccess))
myconfig_file = "/tmp/config"
phaseII_var = "/tmp/Config.hs" phaseII_const = "/tmp/a.hs"
nl = "\n"
writeConfig :: Int -> IO () writeConfig num = do writeFile phaseII_var $ concat ["module Config (config_item) where", nl, "config_item =", show num, nl]
runSuperIO () = system ("echo main | hugs " ++ phaseII_const) >>= \ExitSuccess -> print "Phase II done"
main = readFile myconfig_file >>= writeConfig . read >>= runSuperIO
I did warn you, didn't I? I have a hunch this solution will work with GHC even better than it works with Hugs. Perhaps we can even play with some dynamic linking tricks (like shared object initializers, etc). BTW, the solution above is similar in spirit to the following trick in C++: Config config; int main() { /* pure functional C++ code here -- yes, it exists*/} the constructor for 'config' is guaranteed to run before main(). Perhaps someone will implement Staged Haskell one day?