Ticket #110: Exception.txt

File Exception.txt, 10.0 KB (added by brianh@…, 6 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
33module 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
77import Prelude hiding (ioError, catch)
78import qualified Control.Exception as C
79import Control.Monad.Trans
80import Control.Monad.State
81import Control.Monad.Reader
82import Control.Monad.Writer
83import Control.Monad.RWS
84import Control.Monad.Error
85import Control.Monad.List
86import Control.Concurrent (ThreadId)
87import Data.Typeable
88import Data.Monoid
89import Data.Dynamic
90
91type Exception = C.Exception
92
93ioError :: MonadIO m => IOError -> m a
94ioError i = liftIO $ C.ioError i
95
96evaluate :: MonadIO m => a -> m a
97evaluate a = liftIO $ C.evaluate a
98
99getUncaughtExceptionHandler :: MonadIO m => m (Exception -> m ())
100getUncaughtExceptionHandler = do
101            e_io <- liftIO $ C.getUncaughtExceptionHandler
102            return $ \e -> liftIO (e_io e)
103
104throwM :: MonadIO m => Exception -> m a
105throwM e = liftIO $ C.throwIO e
106
107throwTo :: MonadIO m => ThreadId -> Exception -> m ()
108throwTo t e = liftIO $ C.throwTo t e
109
110throwDynTo :: (MonadIO m, Typeable exception) => ThreadId -> exception -> m ()
111throwDynTo 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
117class 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
127instance 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
137instance 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
150instance 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
160instance (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                               
175instance (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
190instance (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
203instance 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
218catchDyn :: (Typeable exception, MonadException m) => m a -> (exception -> m a) -> m a
219catchDyn 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
228handle :: MonadException m => (Exception -> m a) -> m a -> m a
229handle x y = catch y x
230
231handleDyn :: (Typeable exception, MonadException m) => (exception -> m a) -> m a -> m a
232handleDyn x y = catchDyn y x
233   
234handleJust :: MonadException m => (Exception -> Maybe b) -> (b -> m a) -> m a -> m a
235handleJust x y z = catchJust x z y
236
237bracket :: MonadException m => m a -> (a -> m b) -> (a -> m c) -> m c
238bracket 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
247bracket_ :: MonadException m => m a -> m b -> m c -> m c
248bracket_ before after thing = bracket before (const after) (const thing)
249
250finally :: MonadException m => m a -> m b -> m a
251finally a sequel =
252    block $ do
253        r <- catch
254                 (unblock a)
255                 (\e -> do { sequel; C.throw e })
256        sequel
257        return r
258
259catchJust :: MonadException m => (Exception -> Maybe b) -> m a -> (b -> m a) -> m a
260catchJust 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
265try :: MonadException m => m a -> m (Either Exception a)
266try a = catch (a >>= \ v -> return (Right v)) (\e -> return (Left e))
267
268tryJust :: MonadException m => (Exception -> Maybe b) -> m a -> m (Either b a)
269tryJust 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)