Ticket #110: Exception.txt

File Exception.txt, 10.0 kB (added by brianh@metamilk.com, 3 years ago)

Generalised wrapper for Control.Exception (rename to .hs)

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