| 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) |
|---|