| 1 |
----------------------------------------------------------------------------- |
|---|
| 2 |
-- | |
|---|
| 3 |
-- Module : Prime.Exception |
|---|
| 4 |
-- Copyright : (c) The University of Glasgow 2001 |
|---|
| 5 |
-- (c) Brian Hulley 2006 |
|---|
| 6 |
-- License : BSD-style (see the file libraries/base/LICENSE) |
|---|
| 7 |
-- |
|---|
| 8 |
-- Maintainer : -- not in the library at the moment! |
|---|
| 9 |
-- Stability : experimental |
|---|
| 10 |
-- Portability : non-portable |
|---|
| 11 |
-- |
|---|
| 12 |
-- This module provides support for raising and catching both built-in |
|---|
| 13 |
-- and user-defined exceptions in user defined monads. It is adapted from |
|---|
| 14 |
-- Control.Exception and extends the code supplied by oleg at pobox.com on the |
|---|
| 15 |
-- Haskell mailing list to lift all functions in Control.Exception to all |
|---|
| 16 |
-- monads formed by applying common monad transformers to monads based on IO. |
|---|
| 17 |
-- |
|---|
| 18 |
-- Refs: <http://www.haskell.org/pipermail/haskell/2006-February/017547.html> |
|---|
| 19 |
-- |
|---|
| 20 |
-- To use this you should hide all the conflicting Prelude functions or |
|---|
| 21 |
-- disable implicit Prelude and use a minimal Prelude as suggested in |
|---|
| 22 |
-- <http://hackage.haskell.org/trac/haskell-prime/wiki/Prelude> |
|---|
| 23 |
-- |
|---|
| 24 |
-- THIS MODULE HAS NOT BEEN FULLY TESTED AND IS JUST PROVIDED AS AN |
|---|
| 25 |
-- EXAMPLE FOR DISCUSSION. In particular, you should read Oleg's comments above |
|---|
| 26 |
-- regarding semantics of the different monads under exceptions, and if in |
|---|
| 27 |
-- doubt, consider ReaderT instead of StateT. |
|---|
| 28 |
-- |
|---|
| 29 |
-- If you find any bugs in this module, please add a note to the Haskell' ticket |
|---|
| 30 |
-- or send an email to brianh at metamilk.com |
|---|
| 31 |
----------------------------------------------------------------------------- |
|---|
| 32 |
|
|---|
| 33 |
module Prime.Exception |
|---|
| 34 |
( MonadException(..) |
|---|
| 35 |
|
|---|
| 36 |
, C.Exception(..) |
|---|
| 37 |
, C.IOException |
|---|
| 38 |
, C.ArithException(..) |
|---|
| 39 |
, C.ArrayException(..) |
|---|
| 40 |
, C.AsyncException(..) |
|---|
| 41 |
|
|---|
| 42 |
, C.mapException |
|---|
| 43 |
, C.ioErrors |
|---|
| 44 |
, C.arithExceptions |
|---|
| 45 |
, C.errorCalls |
|---|
| 46 |
, C.dynExceptions |
|---|
| 47 |
, C.assertions |
|---|
| 48 |
, C.asyncExceptions |
|---|
| 49 |
, C.userErrors |
|---|
| 50 |
|
|---|
| 51 |
, C.assert |
|---|
| 52 |
|
|---|
| 53 |
, ioError |
|---|
| 54 |
, evaluate |
|---|
| 55 |
, getUncaughtExceptionHandler |
|---|
| 56 |
|
|---|
| 57 |
, C.throw |
|---|
| 58 |
, C.throwDyn |
|---|
| 59 |
|
|---|
| 60 |
, throwM |
|---|
| 61 |
, throwTo |
|---|
| 62 |
, throwDynTo |
|---|
| 63 |
|
|---|
| 64 |
, handle |
|---|
| 65 |
, handleDyn |
|---|
| 66 |
, handleJust |
|---|
| 67 |
, catchDyn |
|---|
| 68 |
, catchJust |
|---|
| 69 |
, try |
|---|
| 70 |
, tryJust |
|---|
| 71 |
, bracket |
|---|
| 72 |
, bracket_ |
|---|
| 73 |
, finally |
|---|
| 74 |
|
|---|
| 75 |
) where |
|---|
| 76 |
|
|---|
| 77 |
import Prelude hiding (ioError, catch) |
|---|
| 78 |
import qualified Control.Exception as C |
|---|
| 79 |
import Control.Monad.Trans |
|---|
| 80 |
import Control.Monad.State |
|---|
| 81 |
import Control.Monad.Reader |
|---|
| 82 |
import Control.Monad.Writer |
|---|
| 83 |
import Control.Monad.RWS |
|---|
| 84 |
import Control.Monad.Error |
|---|
| 85 |
import Control.Monad.List |
|---|
| 86 |
import Control.Concurrent (ThreadId) |
|---|
| 87 |
import Data.Typeable |
|---|
| 88 |
import Data.Monoid |
|---|
| 89 |
import Data.Dynamic |
|---|
| 90 |
|
|---|
| 91 |
type Exception = C.Exception |
|---|
| 92 |
|
|---|
| 93 |
ioError :: MonadIO m => IOError -> m a |
|---|
| 94 |
ioError i = liftIO $ C.ioError i |
|---|
| 95 |
|
|---|
| 96 |
evaluate :: MonadIO m => a -> m a |
|---|
| 97 |
evaluate a = liftIO $ C.evaluate a |
|---|
| 98 |
|
|---|
| 99 |
getUncaughtExceptionHandler :: MonadIO m => m (Exception -> m ()) |
|---|
| 100 |
getUncaughtExceptionHandler = do |
|---|
| 101 |
e_io <- liftIO $ C.getUncaughtExceptionHandler |
|---|
| 102 |
return $ \e -> liftIO (e_io e) |
|---|
| 103 |
|
|---|
| 104 |
throwM :: MonadIO m => Exception -> m a |
|---|
| 105 |
throwM e = liftIO $ C.throwIO e |
|---|
| 106 |
|
|---|
| 107 |
throwTo :: MonadIO m => ThreadId -> Exception -> m () |
|---|
| 108 |
throwTo t e = liftIO $ C.throwTo t e |
|---|
| 109 |
|
|---|
| 110 |
throwDynTo :: (MonadIO m, Typeable exception) => ThreadId -> exception -> m () |
|---|
| 111 |
throwDynTo t e = liftIO $ C.throwDynTo t e |
|---|
| 112 |
|
|---|
| 113 |
-- | It is debatable whether or not @MonadException@ should derive from @MonadIO@ since |
|---|
| 114 |
-- none of the functions require @MonadIO@. However a @MonadException@ context will |
|---|
| 115 |
-- usually also involve use of @liftIO@ or @throwM@ etc which needs a @MonadIO@ |
|---|
| 116 |
|
|---|
| 117 |
class MonadIO m => MonadException m where |
|---|
| 118 |
catch :: m a -> (Exception -> m a) -> m a |
|---|
| 119 |
|
|---|
| 120 |
block, unblock :: m a -> m a |
|---|
| 121 |
|
|---|
| 122 |
-- It is debatable whether or not this very low level function should be |
|---|
| 123 |
-- part of the class at all or should just remain tied to the IO monad |
|---|
| 124 |
setUncaughtExceptionHandler :: (Exception -> m ()) -> m () |
|---|
| 125 |
|
|---|
| 126 |
|
|---|
| 127 |
instance MonadException IO where |
|---|
| 128 |
catch = C.catch |
|---|
| 129 |
block = C.block |
|---|
| 130 |
unblock = C.unblock |
|---|
| 131 |
setUncaughtExceptionHandler = C.setUncaughtExceptionHandler |
|---|
| 132 |
|
|---|
| 133 |
-- In the code that follows, om is used to mean "outer monad" so that it is |
|---|
| 134 |
-- clearly distinguished from m which is the type of the inner monad |
|---|
| 135 |
-- Also, "e_om" represents the handler so that it's easy to see that "e_om e" = an outer monad |
|---|
| 136 |
|
|---|
| 137 |
instance MonadException m => MonadException (StateT s m) where |
|---|
| 138 |
catch om e_om = StateT $ \s -> catch (runStateT om s) (\e -> runStateT (e_om e) s) |
|---|
| 139 |
|
|---|
| 140 |
block om = StateT $ \s -> block (runStateT om s) |
|---|
| 141 |
unblock om = StateT $ \s -> unblock (runStateT om s) |
|---|
| 142 |
|
|---|
| 143 |
setUncaughtExceptionHandler e_om = StateT $ \s -> do |
|---|
| 144 |
setUncaughtExceptionHandler $ \e -> do |
|---|
| 145 |
runStateT (e_om e) s |
|---|
| 146 |
return () |
|---|
| 147 |
return ((), s) |
|---|
| 148 |
|
|---|
| 149 |
|
|---|
| 150 |
instance MonadException m => MonadException (ReaderT r m) where |
|---|
| 151 |
catch om e_om = ReaderT $ \r -> catch (runReaderT om r) (\e -> runReaderT (e_om e) r) |
|---|
| 152 |
|
|---|
| 153 |
block om = ReaderT $ \r -> block (runReaderT om r) |
|---|
| 154 |
unblock om = ReaderT $ \r -> unblock (runReaderT om r) |
|---|
| 155 |
|
|---|
| 156 |
setUncaughtExceptionHandler e_om = ReaderT $ \r -> |
|---|
| 157 |
setUncaughtExceptionHandler (\e -> runReaderT (e_om e) r) |
|---|
| 158 |
|
|---|
| 159 |
|
|---|
| 160 |
instance (MonadException m, Monoid w) => MonadException (WriterT w m) where |
|---|
| 161 |
catch om e_om = WriterT $ catch (runWriterT om) (\e -> runWriterT (e_om e)) |
|---|
| 162 |
|
|---|
| 163 |
block om = WriterT $ block (runWriterT om) |
|---|
| 164 |
unblock om = WriterT $ unblock (runWriterT om) |
|---|
| 165 |
|
|---|
| 166 |
setUncaughtExceptionHandler e_om = do |
|---|
| 167 |
(_,w) <- listen (return ()) |
|---|
| 168 |
WriterT $ do |
|---|
| 169 |
setUncaughtExceptionHandler (\e -> do |
|---|
| 170 |
runWriterT (e_om e) |
|---|
| 171 |
return ()) |
|---|
| 172 |
return ((), w) |
|---|
| 173 |
|
|---|
| 174 |
|
|---|
| 175 |
instance (MonadException m, Monoid w) => MonadException (RWST r w s m) where |
|---|
| 176 |
catch om e_om = RWST $ \r s -> catch (runRWST om r s) (\e -> runRWST (e_om e) r s) |
|---|
| 177 |
|
|---|
| 178 |
block om = RWST $ \r s -> block (runRWST om r s) |
|---|
| 179 |
unblock om = RWST $ \r s -> unblock (runRWST om r s) |
|---|
| 180 |
|
|---|
| 181 |
setUncaughtExceptionHandler e_om = do |
|---|
| 182 |
(_,w) <- listen (return ()) |
|---|
| 183 |
RWST $ \r s -> do |
|---|
| 184 |
setUncaughtExceptionHandler (\e -> do |
|---|
| 185 |
runRWST (e_om e) r s |
|---|
| 186 |
return ()) |
|---|
| 187 |
return ((),s,w) |
|---|
| 188 |
|
|---|
| 189 |
|
|---|
| 190 |
instance (MonadException m, Error e) => MonadException (ErrorT e m) where |
|---|
| 191 |
catch om e_om = ErrorT $ catch (runErrorT om) (\e -> runErrorT (e_om e)) |
|---|
| 192 |
|
|---|
| 193 |
block om = ErrorT $ block (runErrorT om) |
|---|
| 194 |
unblock om = ErrorT $ unblock (runErrorT om) |
|---|
| 195 |
|
|---|
| 196 |
setUncaughtExceptionHandler e_om = ErrorT $ do |
|---|
| 197 |
setUncaughtExceptionHandler (\e -> do |
|---|
| 198 |
runErrorT (e_om e) |
|---|
| 199 |
return ()) |
|---|
| 200 |
return (Right ()) |
|---|
| 201 |
|
|---|
| 202 |
|
|---|
| 203 |
instance MonadException m => MonadException (ListT m) where |
|---|
| 204 |
catch om e_om = ListT $ catch (runListT om) (\e -> runListT (e_om e)) |
|---|
| 205 |
|
|---|
| 206 |
block om = ListT $ block (runListT om) |
|---|
| 207 |
unblock om = ListT $ unblock (runListT om) |
|---|
| 208 |
|
|---|
| 209 |
setUncaughtExceptionHandler e_om = ListT $ do |
|---|
| 210 |
setUncaughtExceptionHandler (\e -> do |
|---|
| 211 |
runListT (e_om e) |
|---|
| 212 |
return ()) |
|---|
| 213 |
return [()] |
|---|
| 214 |
|
|---|
| 215 |
|
|---|
| 216 |
-- The following are pasted from Control.Exception with IO replaced by an instance of MonadException |
|---|
| 217 |
|
|---|
| 218 |
catchDyn :: (Typeable exception, MonadException m) => m a -> (exception -> m a) -> m a |
|---|
| 219 |
catchDyn m k = catch m handle |
|---|
| 220 |
where handle ex = case ex of |
|---|
| 221 |
(C.DynException dyn) -> |
|---|
| 222 |
case fromDynamic dyn of |
|---|
| 223 |
Just exception -> k exception |
|---|
| 224 |
Nothing -> C.throw ex |
|---|
| 225 |
_ -> C.throw ex |
|---|
| 226 |
|
|---|
| 227 |
|
|---|
| 228 |
handle :: MonadException m => (Exception -> m a) -> m a -> m a |
|---|
| 229 |
handle x y = catch y x |
|---|
| 230 |
|
|---|
| 231 |
handleDyn :: (Typeable exception, MonadException m) => (exception -> m a) -> m a -> m a |
|---|
| 232 |
handleDyn x y = catchDyn y x |
|---|
| 233 |
|
|---|
| 234 |
handleJust :: MonadException m => (Exception -> Maybe b) -> (b -> m a) -> m a -> m a |
|---|
| 235 |
handleJust x y z = catchJust x z y |
|---|
| 236 |
|
|---|
| 237 |
bracket :: MonadException m => m a -> (a -> m b) -> (a -> m c) -> m c |
|---|
| 238 |
bracket before after thing = |
|---|
| 239 |
block $ do |
|---|
| 240 |
a <- before |
|---|
| 241 |
r <- catch |
|---|
| 242 |
(unblock (thing a)) |
|---|
| 243 |
(\e -> do { after a; C.throw e }) |
|---|
| 244 |
after a |
|---|
| 245 |
return r |
|---|
| 246 |
|
|---|
| 247 |
bracket_ :: MonadException m => m a -> m b -> m c -> m c |
|---|
| 248 |
bracket_ before after thing = bracket before (const after) (const thing) |
|---|
| 249 |
|
|---|
| 250 |
finally :: MonadException m => m a -> m b -> m a |
|---|
| 251 |
finally a sequel = |
|---|
| 252 |
block $ do |
|---|
| 253 |
r <- catch |
|---|
| 254 |
(unblock a) |
|---|
| 255 |
(\e -> do { sequel; C.throw e }) |
|---|
| 256 |
sequel |
|---|
| 257 |
return r |
|---|
| 258 |
|
|---|
| 259 |
catchJust :: MonadException m => (Exception -> Maybe b) -> m a -> (b -> m a) -> m a |
|---|
| 260 |
catchJust p a handler = catch a handler' |
|---|
| 261 |
where handler' e = case p e of |
|---|
| 262 |
Nothing -> C.throw e |
|---|
| 263 |
Just b -> handler b |
|---|
| 264 |
|
|---|
| 265 |
try :: MonadException m => m a -> m (Either Exception a) |
|---|
| 266 |
try a = catch (a >>= \ v -> return (Right v)) (\e -> return (Left e)) |
|---|
| 267 |
|
|---|
| 268 |
tryJust :: MonadException m => (Exception -> Maybe b) -> m a -> m (Either b a) |
|---|
| 269 |
tryJust p a = do |
|---|
| 270 |
r <- try a |
|---|
| 271 |
case r of |
|---|
| 272 |
Right v -> return (Right v) |
|---|
| 273 |
Left e -> case p e of |
|---|
| 274 |
Nothing -> C.throw e |
|---|
| 275 |
Just b -> return (Left b) |
|---|