-- |
-- Module: Acme.RealWorld
-- Copyright: (c) Joseph Adams 2011
-- Maintainer: joeyadams3.14159@gmail.com
-- Portability: GHC-only
--
-- Primitives for manipulating the state of the universe.
{-# LANGUAGE MagicHash, UnboxedTuples #-}
module Acme.RealWorld (
-- * The RealWorld type
RealWorld,
-- * Universe manipulation primitives
getWorld,
putWorld,
execIO,
-- * Derived combinators
hypothetically,
) where
import qualified Control.Exception as E
import GHC.IO
import GHC.Exts
fromState :: State# RealWorld -> RealWorld
fromState = unsafeCoerce#
toState :: RealWorld -> State# RealWorld
toState = unsafeCoerce#
-- | Retrieve the current state of the universe.
getWorld :: IO RealWorld
getWorld = IO (\s -> (# s, fromState s #))
-- | Set the current state of the universe. Program values are not affected by
-- this operation, but the rest of the universe is.
--
-- 'putWorld' may not be called on the same state twice (this is enforced by
-- the runtime system). Otherwise, it would be possible to trap the universe
-- in a temporal loop:
--
-- >getWorld >>= forever . putWorld
putWorld :: RealWorld -> IO ()
putWorld s' = IO (\_ -> (# toState s', () #))
-- | Given an action, construct a function that, given a state of the universe,
-- returns the state of the universe after the action has occurred.
--
-- Example:
--
-- >main = do
-- > let f = execIO $ putStrLn "Second"
-- > g = execIO $ putStrLn "First"
-- > getWorld >>= putWorld . f . g
execIO :: IO a -> RealWorld -> RealWorld
execIO (IO k) = \w -> case k (toState w) of (# s', _ #) -> fromState s'
-- | Perform an action and return its value, but undo any side effects to the
-- universe. Thus, it appears to return instantly, regardless of how long the
-- action would take to run.
--
-- The caller must ensure that the program /would/ have enough time to perform
-- the computation. Otherwise, either an exception will be thrown, or the
-- operation will block because it never gets a chance to restore the original
-- state of the universe.
hypothetically :: IO a -> IO a
hypothetically action = E.bracket getWorld putWorld (\_ -> action)