
G'day all. On Thu, Sep 26, 2002 at 12:06:36AM +0100, Liyang Hu wrote:
The problem I'm having is with the preferences: How do I make it available throughout the entire program? (FWIW, most of the work is effectively done inside the IO monad.) I could explicitly pass the record around everywhere, but that seems a trifle inelegant.
My current solution is to use a global ('scuse my terminology, I'm not sure that's the right word to use here) variable of type IORef Config obtained through unsafePerformIO. It works, but strikes me as a rather barbaric solution to a seemingly tame enough problem...
One solution is to do precisely as you suggested, using a state monad to wrap the IORef. For example: import Control.Monad.Reader import Data.IORef type MyIO a = ReaderT (IORef Config) IO a main = do config <- readConfigurationStuff configref <- newIORef config runReaderT configref main' getConfig :: MyIO Config getConfig = do configref <- ask liftIO (readIORef configref) -- Same as above, but you can supply a projection function. getsConfig :: (Config -> a) -> MyIO a getsConfig f = do config <- getConfig return (f config) -- ...and this is where the code REALLY starts. main' :: MyIO () main' = do config <- getConfig liftIO (putStrLn (show config)) -- etc You can wrap whole slabs of existing code in liftIO if it uses IO but does not need to read the configuration. There's also a much uglier solution which I occasionally use if I need an "ad hoc" global variable. Rather than using IORefs, I use Strings as keys. The code is here: http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/hfl/hfl/ioext/ Example of use: import IOGlobal main :: IO () main = do writeIOGlobalM "foo" "Foo data" writeIOGlobalM "bar" ("Bar", ["data"]) foo <- readIOGlobalM "foo" putStrLn foo bar <- readIOGlobalM "bar" putStrLn (show (bar :: (String, [String]))) Cheers, Andrew Bromage