{-# OPTIONS_LHC -N #-} module Lhc.ACIO where import Lhc.IORef import Lhc.IO import Lhc.Monad newtype ACIO a = ACIO (IO a) deriving(Monad,Functor) unsafeIOToACIO :: IO a -> ACIO a unsafeIOToACIO x = ACIO x acioToIO :: ACIO a -> IO a acioToIO (ACIO x) = x newIORefAC :: a -> ACIO (IORef a) newIORefAC a = unsafeIOToACIO (newIORef a) runOnce :: IO a -> ACIO (IO a) runOnce action = do ref <- newIORefAC Nothing return $ do v <- readIORef ref case v of Just v -> return v Nothing -> do v <- action writeIORef ref v return v {-@Extensions # Top Level Actions Lhc supports monadic actions declared at the top level of your module. These can be used to do things such as initialize IORefs or allocate static data. An example of a top level action is the following. import Lhc.ACIO import Data.IORef ref <- newIORefAC 0 count = do modifyIORef ref (1 +) readIORef ref >>= print main = do count count count Which will print 1, 2, and 3. A special monad ACIO (which stands for Affine Central IO) is provided to restrict what may take place in top level actions. Basically, top level actions can only consist of IO that can be omitted or reordered without changing the meaning of a program. In practice, this means that it does not matter whether such actions are all performed at the beginning or are only computed once on demand. If you need to use arbitrary IO, a utility function 'runOnce' is provided. using it you can ensure arbitrary IO actions are run only once and the return values shared, however you must access the value inside the IO monad, thus ensuring program integrity. An example using a hypothetical GUI library is below. import Lhc.ACIO getWindow <- runOnce $ do connection <- newGUIConnection window <- createWindow (640,480) setTitle window "My Global Window" return window main = do w <- getWindow draw w "Hello!" Note, top level global variables can be indicative of design issues. In general, they should only be used when necessary to interface with an external library, opaque uses inside a library where the shared state can not be externally observed, or inside your Main program as design dictates. -}