{-# language BlockArguments #-}
{-# language DataKinds #-}
{-# language FlexibleContexts #-}
{-# language GADTs #-}
{-# language LambdaCase #-}
{-# language RankNTypes #-}
{-# language TypeOperators #-}
module IO.Effects.Exception
(
runExceptions
, throwIO
, catch
, catchIO
, catchAny
, catchJust
, handle
, handleIO
, handleAny
, handleJust
, try
, tryIO
, tryAny
, tryJust
, Handler(..)
, catches
, onException
, bracket
, bracket_
, finally
, withException
, bracketOnError
, bracketOnError_
, isSyncException
, mask
, mask_
, uninterruptibleMask
, uninterruptibleMask_
, evaluate
, Exceptions(..)
) where
import Control.Exception ( Exception, IOException, SomeException( SomeException ), SomeAsyncException( SomeAsyncException ), fromException, toException )
import qualified Control.Exception as EUnsafe
import IO.Effects.Internal
data Exceptions m a where
Catch :: Exception e => m a -> ( e -> m a ) -> Exceptions m a
Evaluate :: a -> Exceptions m a
Mask :: ( ( forall a. m a -> m a ) -> m b ) -> Exceptions m b
ThrowIO :: Exception e => e -> Exceptions m a
UninterruptibleMask :: ( ( forall a. m a -> m a ) -> m b ) -> Exceptions m b
runExceptions
:: ProgramWithHandler Exceptions es a
-> Program es ( Either SomeException a )
runExceptions p =
go ( tryAny p )
where
go =
interpret \case
Catch m f ->
Program ( EUnsafe.catch ( programToIO m ) ( programToIO . f ) )
Evaluate a ->
Program ( EUnsafe.evaluate a )
Mask f ->
Program ( EUnsafe.mask ( \restore -> programToIO ( f ( Program . restore . programToIO ) ) ) )
UninterruptibleMask f ->
Program ( EUnsafe.uninterruptibleMask ( \restore -> programToIO ( f ( Program . restore . programToIO ) ) ) )
ThrowIO e ->
Program ( EUnsafe.throwIO e )
catch
:: ( Exception e, Member Exceptions es )
=> Program es a -> ( e -> Program es a ) -> Program es a
catch m f =
send ( m `Catch` f' )
where
f' e =
if isSyncException e then
f e
else
throwIO e
catchIO
:: Member Exceptions es
=> Program es a -> ( IOException -> Program es a ) -> Program es a
catchIO =
catch
catchAny
:: Member Exceptions es
=> Program es a -> ( SomeException -> Program es a ) -> Program es a
catchAny =
catch
catchJust
:: ( Member Exceptions es, Exception e )
=> ( e -> Maybe b )
-> Program es a
-> ( b -> Program es a )
-> Program es a
catchJust f a b =
a `catch` \e -> maybe ( throwIO e ) b $ f e
handle
:: ( Member Exceptions es, Exception e )
=> ( e -> Program es a ) -> Program es a -> Program es a
handle =
flip catch
handleIO
:: Member Exceptions es
=> ( IOException -> Program es a ) -> Program es a -> Program es a
handleIO =
flip catch
handleAny
:: Member Exceptions es
=> ( SomeException -> Program es a ) -> Program es a -> Program es a
handleAny =
flip catch
handleJust
:: ( Member Exceptions es, Exception e )
=> ( e -> Maybe b )
-> ( b -> Program es a )
-> Program es a
-> Program es a
handleJust f =
flip ( catchJust f )
try
:: ( Member Exceptions es, Exception e )
=> Program es a -> Program es ( Either e a )
try f =
catch ( fmap Right f ) ( return . Left )
tryIO
:: Member Exceptions es
=> Program es a -> Program es ( Either IOException a )
tryIO =
try
tryAny
:: Member Exceptions es
=> Program es a -> Program es ( Either SomeException a )
tryAny =
try
tryJust
:: ( Member Exceptions es, Exception e )
=> ( e -> Maybe b ) -> Program es a -> Program es ( Either b a )
tryJust f a =
catch ( Right `fmap` a ) ( \e -> maybe ( throwIO e ) ( return . Left ) ( f e ) )
data Handler m a where
Handler :: Exception e => ( e -> m a ) -> Handler m a
catchesHandler
:: Member Exceptions es
=> [ Handler ( Program es ) a ] -> SomeException -> Program es a
catchesHandler handlers e =
foldr tryHandler ( throwIO e ) handlers
where
tryHandler ( Handler handler ) res =
maybe res handler ( fromException e )
catches
:: Member Exceptions es
=> Program es a -> [ Handler ( Program es ) a ] -> Program es a
catches io handlers =
io `catch` catchesHandler handlers
evaluate :: Member Exceptions es => a -> Program es a
evaluate =
send . Evaluate
bracket
:: Member Exceptions es
=> Program es a
-> ( a -> Program es b )
-> ( a -> Program es c )
-> Program es c
bracket before after thing = mask \restore -> do
x <-
before
res1 <-
tryAny ( restore ( thing x ) )
case res1 of
Left e1 -> do
_ <-
tryAny ( uninterruptibleMask_ ( after x ) )
throwIO e1
Right y ->
y <$ uninterruptibleMask_ ( after x )
bracket_
:: Member Exceptions es
=> Program es a -> Program es b -> Program es c -> Program es c
bracket_ before after thing =
bracket before ( const after ) ( const thing )
bracketOnError
:: Member Exceptions es
=> Program es a -> ( a -> Program es b ) -> ( a -> Program es c ) -> Program es c
bracketOnError before after thing = mask \restore -> do
x <-
before
res1 <-
tryAny ( restore ( thing x ) )
case res1 of
Left e1 -> do
_ <-
tryAny ( uninterruptibleMask_ ( after x ) )
throwIO e1
Right y ->
return y
bracketOnError_
:: Member Exceptions es
=> Program es a -> Program es b -> Program es c -> Program es c
bracketOnError_ before after thing =
bracketOnError before ( const after ) ( const thing )
finally
:: Member Exceptions es
=> Program es a -> Program es b -> Program es a
finally thing after = uninterruptibleMask \restore -> do
res1 <-
tryAny ( restore thing )
case res1 of
Left e1 -> do
_ <-
tryAny after
throwIO e1
Right x -> do
x <$ after
withException
:: ( Member Exceptions es, Exception e )
=> Program es a -> ( e -> Program es b ) -> Program es a
withException thing after = uninterruptibleMask \restore -> do
res1 <-
try ( restore thing )
case res1 of
Left e1 -> do
_ <-
tryAny ( after e1 )
throwIO e1
Right x ->
return x
onException
:: Member Exceptions es
=> Program es a -> Program es b -> Program es a
onException thing after =
withException thing \SomeException{} -> after
throwIO :: ( Member Exceptions es, Exception e ) => e -> Program es a
throwIO =
send . ThrowIO
mask
:: Member Exceptions es
=> ( ( forall x. Program es x -> Program es x ) -> Program es a )
-> Program es a
mask f =
send ( Mask f )
uninterruptibleMask
:: Member Exceptions es
=> ( ( forall x. Program es x -> Program es x ) -> Program es a )
-> Program es a
uninterruptibleMask f =
send ( UninterruptibleMask f )
mask_ :: Member Exceptions es => Program es a -> Program es a
mask_ m =
mask \_ -> m
uninterruptibleMask_ :: Member Exceptions es => Program es a -> Program es a
uninterruptibleMask_ m =
uninterruptibleMask \_ -> m
isSyncException :: Exception e => e -> Bool
isSyncException e =
case fromException ( toException e ) of
Just SomeAsyncException{} ->
False
Nothing ->
True