--
-- The IO Monad with an environment
--
+{-# LANGUAGE UndecidableInstances #-}
module IOEnv (
IOEnv, -- Instance of Monad
-- Errors
failM, failWithM,
+ IOEnvFailure(..),
-- Getting at the environment
getEnv, setEnv, updEnv,
IORef, newMutVar, readMutVar, writeMutVar, updMutVar
) where
-import Panic ( try, tryUser, tryMost, Exception(..) )
+import Exception
+import Panic
import Data.IORef ( IORef, newIORef, readIORef, writeIORef, modifyIORef )
+import Data.Typeable
import System.IO.Unsafe ( unsafeInterleaveIO )
import System.IO ( fixIO )
+import Control.Monad
import MonadUtils
----------------------------------------------------------------------
thenM_ (IOEnv m) f = IOEnv (\ env -> do { m env ; unIOEnv f env })
failM :: IOEnv env a
-failM = IOEnv (\ _ -> ioError (userError "IOEnv failure"))
+failM = IOEnv (\ _ -> throwIO IOEnvFailure)
failWithM :: String -> IOEnv env a
failWithM s = IOEnv (\ _ -> ioError (userError s))
+data IOEnvFailure = IOEnvFailure
+ deriving Typeable
+instance Show IOEnvFailure where
+ show IOEnvFailure = "IOEnv failure"
+
+instance Exception IOEnvFailure
----------------------------------------------------------------------
-- Fundmantal combinators specific to the monad
---------------------------
-tryM :: IOEnv env r -> IOEnv env (Either Exception r)
+tryM :: IOEnv env r -> IOEnv env (Either IOEnvFailure r)
-- Reflect UserError exceptions (only) into IOEnv monad
-- Other exceptions are not caught; they are simply propagated as exns
--
-- to UserErrors. But, say, pattern-match failures in GHC itself should
-- not be caught here, else they'll be reported as errors in the program
-- begin compiled!
-tryM (IOEnv thing) = IOEnv (\ env -> tryUser (thing env))
+tryM (IOEnv thing) = IOEnv (\ env -> tryIOEnvFailure (thing env))
+
+tryIOEnvFailure :: IO a -> IO (Either IOEnvFailure a)
+tryIOEnvFailure = try
-tryAllM :: IOEnv env r -> IOEnv env (Either Exception r)
+-- XXX We shouldn't be catching everything, e.g. timeouts
+tryAllM :: IOEnv env r -> IOEnv env (Either SomeException r)
-- Catch *all* exceptions
-- This is used when running a Template-Haskell splice, when
-- even a pattern-match failure is a programmer error
tryAllM (IOEnv thing) = IOEnv (\ env -> try (thing env))
-tryMostM :: IOEnv env r -> IOEnv env (Either Exception r)
+tryMostM :: IOEnv env r -> IOEnv env (Either SomeException r)
tryMostM (IOEnv thing) = IOEnv (\ env -> tryMost (thing env))
---------------------------
----------------------------------------------------------------------
+-- MonadPlus
+----------------------------------------------------------------------
+
+-- For use if the user has imported Control.Monad.Error from MTL
+-- Requires UndecidableInstances
+#if __GLASGOW_HASKELL__ > 606
+-- for some reason, this doesn't compile with GHC 6.6:
+-- utils/IOEnv.hs:144:33:
+-- No instance for (MonadPlus IO)
+-- arising from use of `mplus' at utils/IOEnv.hs:144:33-67
+instance MonadPlus IO => MonadPlus (IOEnv env) where
+ mzero = IOEnv (const mzero)
+ m `mplus` n = IOEnv (\env -> unIOEnv m env `mplus` unIOEnv n env)
+#endif
+
+----------------------------------------------------------------------
-- Accessing input/output
----------------------------------------------------------------------
-- (for efficiency)
----------------------------------------------------------------------
-{-# -- SPECIALIZE mapM :: (a -> IOEnv env b) -> [a] -> IOEnv env [b] #-}
-{-# -- SPECIALIZE mapM_ :: (a -> IOEnv env b) -> [a] -> IOEnv env () #-}
-{-# -- SPECIALIZE mapSndM :: (b -> IOEnv env c) -> [(a,b)] -> IOEnv env [(a,c)] #-}
-{-# -- SPECIALIZE sequence :: [IOEnv env a] -> IOEnv env [a] #-}
-{-# -- SPECIALIZE sequence_ :: [IOEnv env a] -> IOEnv env () #-}
-{-# -- SPECIALIZE foldlM :: (a -> b -> IOEnv env a) -> a -> [b] -> IOEnv env a #-}
-{-# -- SPECIALIZE foldrM :: (b -> a -> IOEnv env a) -> a -> [b] -> IOEnv env a #-}
-{-# -- SPECIALIZE mapAndUnzipM :: (a -> IOEnv env (b,c)) -> [a] -> IOEnv env ([b],[c]) #-}
-{-# -- SPECIALIZE mapAndUnzip3M :: (a -> IOEnv env (b,c,d)) -> [a] -> IOEnv env ([b],[c],[d]) #-}
-{-# -- SPECIALIZE zipWithM :: (a -> b -> IOEnv env c) -> [a] -> [b] -> IOEnv env [c] #-}
-{-# -- SPECIALIZE zipWithM_ :: (a -> b -> IOEnv env c) -> [a] -> [b] -> IOEnv env () #-}
-{-# -- SPECIALIZE anyM :: (a -> IOEnv env Bool) -> [a] -> IOEnv env Bool #-}
-{-# -- SPECIALIZE when :: Bool -> IOEnv env a -> IOEnv env () #-}
-{-# -- SPECIALIZE unless :: Bool -> IOEnv env a -> IOEnv env () #-}
+-- {-# SPECIALIZE mapM :: (a -> IOEnv env b) -> [a] -> IOEnv env [b] #-}
+-- {-# SPECIALIZE mapM_ :: (a -> IOEnv env b) -> [a] -> IOEnv env () #-}
+-- {-# SPECIALIZE mapSndM :: (b -> IOEnv env c) -> [(a,b)] -> IOEnv env [(a,c)] #-}
+-- {-# SPECIALIZE sequence :: [IOEnv env a] -> IOEnv env [a] #-}
+-- {-# SPECIALIZE sequence_ :: [IOEnv env a] -> IOEnv env () #-}
+-- {-# SPECIALIZE foldlM :: (a -> b -> IOEnv env a) -> a -> [b] -> IOEnv env a #-}
+-- {-# SPECIALIZE foldrM :: (b -> a -> IOEnv env a) -> a -> [b] -> IOEnv env a #-}
+-- {-# SPECIALIZE mapAndUnzipM :: (a -> IOEnv env (b,c)) -> [a] -> IOEnv env ([b],[c]) #-}
+-- {-# SPECIALIZE mapAndUnzip3M :: (a -> IOEnv env (b,c,d)) -> [a] -> IOEnv env ([b],[c],[d]) #-}
+-- {-# SPECIALIZE zipWithM :: (a -> b -> IOEnv env c) -> [a] -> [b] -> IOEnv env [c] #-}
+-- {-# SPECIALIZE zipWithM_ :: (a -> b -> IOEnv env c) -> [a] -> [b] -> IOEnv env () #-}
+-- {-# SPECIALIZE anyM :: (a -> IOEnv env Bool) -> [a] -> IOEnv env Bool #-}
+-- {-# SPECIALIZE when :: Bool -> IOEnv env a -> IOEnv env () #-}
+-- {-# SPECIALIZE unless :: Bool -> IOEnv env a -> IOEnv env () #-}