packages feed

vintage-basic-1.0.3: src/Control/Monad/CPST/ExceptionHandlers.hs

-- | Exception handlers for the CPST monad, both \'catching\' (within an expression)
-- and \'trapping\' (from the handler's continuation).

module Control.Monad.CPST.ExceptionHandlers where

import Control.Monad.CPST

-- The inward (catching) exception handlers --

-- | Captures an exception that occurs within the specified computation.
-- Like @try-catch@ in Java, but without the filtering of exceptions.
capture :: Monad m
    => (o1 -> CPST o m i) -- ^ the exception handler
    -> CPST o1 m o1       -- ^ the computation within which to capture exceptions
    -> CPST o m i
capture f m = reset m >>= f

-- | Forces a computation to occur after another one, even if the first one is
-- aborted. Like @try-finally@ in Java.
tack :: Monad m
    => CPST o m i   -- ^ the computation to tack on (@finally@ clause)
    -> CPST o1 m o1 -- ^ the computation to wrap (@try@ clause)
    -> CPST o m i
tack m = capture (\_ -> m)

-- | Catches exceptions in a computation that satisfy a predicate, and applies
-- a function to them. If the exception does not match the predicate, it
-- rethrows the exception. This is like @try-catch@ in Java.
handle :: Monad m
    => (o -> Bool)       -- ^ the condition for matching the exception
    -> (o -> CPST o m o) -- ^ the exception handler
    -> CPST o m o        -- ^ the computation within which to catch exceptions
    -> CPST o m o
handle t f = capture (\x -> if t x then f x else abort x)

-- The outward (trapping) exception handlers --

-- | Captures an exception that occurs in the continuation of this statement.
-- Like a @trap@ statement in Bourne shell or Windows PowerShell, but without
-- discriminating over the exception type.
captureAtEnd :: Monad m
    => (o -> CPST o m o) -- ^ the exception handler
    -> CPST o m ()
captureAtEnd f = shift (\k -> reset (k ()) >>= f)
-- ALTERNATELY: captureAtEnd f = CPST (\k -> do x <- k (); runCPST (f x))
-- ALTERNATELY: captureAtEnd f = callCC (\k -> reset (k ()) >>= f >>= abort)

-- | Forces a computation to occur at the end of execution. Like an @END@
-- block in Perl or @atexit()@ in C.
tackAtEnd :: Monad m
    => CPST o m o  -- ^ the computation to tack at the end of execution
    -> CPST o m ()
tackAtEnd m = captureAtEnd (\_ -> m)
-- ALTERNATELY: tackAtEnd m = CPST (\k -> do k (); runCPST m)

-- | Handles an exception that occurs in the continuation of this statement.
-- Like a @trap@ statement in Bourne shell or Windows PowerShell.
-- The exception is only caught if it matches a predicate. Otherwise, it is
-- passed on to the next outer handler.
handleAtEnd :: Monad m
    => (o -> Bool)       -- ^ the condition for matching the exception
    -> (o -> CPST o m o) -- ^ the exception handler
    -> CPST o m ()
handleAtEnd t f = captureAtEnd (\x -> if t x then f x else return x)

-- Some convenient aliases

-- | An alias for 'abort'.
raise :: Monad m => o -> CPST o m a
raise = abort

-- | An alias for 'captureAtEnd'.
install :: Monad m => (o -> CPST o m o) -> CPST o m ()
install = captureAtEnd