haskellglobal-variablespurely-functionalunsafe-perform-io

A way to avoid a common use of unsafePerformIO


I often find this pattern in Haskell code:

options :: MVar OptionRecord
options = unsafePerformIO $ newEmptyMVar

...

doSomething :: Foo -> Bar
doSomething = unsafePerformIO $ do
  opt <- readMVar options
  doSomething' where ...

Basically, one has a record of options or something similar, that is initially set at the program's beginning. As the programmer is lazy, he doesn't want to carry the options record all over the program. He defines an MVar to keep it - defined by an ugly use of unsafePerformIO. The programmer ensures, that the state is set only once and before any operation has taken place. Now each part of the program has to use unsafePerformIO again, just to extract the options.

In my opinion, such a variable is considered pragmatically pure (don't beat me). Is there a library that abstracts this concept away and ensures that the variable is set only once, i.e. that no call is done before that initialization and that one doesn't have to write unsafeFireZeMissilesAndMakeYourCodeUglyAnd DisgustingBecauseOfThisLongFunctionName


Solution

  • If you are using MVar for holding settings or something similar, why don't you try reader monad?

    foo :: ReaderT OptionRecord IO ()
    foo = do
        options <- ask
        fireMissiles
    
    main = runReaderT foo (OptionRecord "foo")
    

    (And regular Reader if you don't require IO :P)