{- |
Synchronous exceptions immediately abort a series of computations.
We provide monads for describing this behaviour.
In contrast to ErrorT from @mtl@ or @transformers@ package
we do not pose restrictions on the exception type.
How to tell, that a function can possibly throw more than one (kind of) exception?
If you would use the exception type @(Either ParserException IOError)@
then this is different from @(Either IOError ParserException)@.
Thus we recommned using type classes for exceptions.
Then you can use one type containing all exceptions in an application,
but the type signature still tells which exceptions are actually possible.
Examples:
> parser :: ParserException e => ExceptionalT e ParserMonad a
>
> getLine :: IOException e => ExceptionalT e IO String
>
> fileParser :: (ParserException e, IOException e) => ExceptionalT e IO String
Unfortunately, this way you cannot remove single exceptions
from the constraints by catching them.
You can only remove all of them using 'resolve' or none.
For a more advanced approach,
that allows removing exceptions constraints
by some non-Haskell-98 type hackery,
see the exception package by Joseph Iborra.
-}moduleControl.Monad.Exception.Synchronous(Exceptional(..),fromMaybe,toMaybe,fromEither,toEither,getExceptionNull,switch,force,mapException,mapExceptional,throw,assert,catch,resolve,ExceptionalT(..),fromErrorT,toErrorT,fromEitherT,toEitherT,forceT,mapExceptionT,mapExceptionalT,throwT,assertT,catchT,bracketT,resolveT,tryT,manyT,manyMonoidT,)whereimportControl.Applicative(Applicative(pure,(<*>)))importControl.Monad(liftM,{- MonadPlus(mzero, mplus), -})importControl.Monad.Fix(MonadFix,mfix,)importControl.Monad.Trans.Class(MonadTrans,lift,)importControl.Monad.Trans.Error(ErrorT(ErrorT,runErrorT))importData.Monoid(Monoid,mappend,mempty,Endo(Endo,appEndo),)importPreludehiding(catch,)-- * Plain monad{- |
Like 'Either', but explicitly intended for handling of exceptional results.
In contrast to 'Either' we do not support 'fail'.
Calling 'fail' in the 'Exceptional' monad is an error.
This way, we do not require that an exception can be derived from a 'String',
yet, we require no constraint on the exception type at all.
-}dataExceptionalea=Successa|Exceptionederiving(Show,Eq)fromMaybe::e->Maybea->ExceptionaleafromMaybee=maybe(Exceptione)SuccessfromEither::Eitherea->ExceptionaleafromEither=eitherExceptionSuccesstoMaybe::Exceptionalea->MaybeatoMaybe=switch(constNothing)JusttoEither::Exceptionalea->EithereatoEitherx=casexofSuccessa->RightaExceptione->Lefte-- | useful in connection with 'Control.Monad.Exception.Asynchronous.continue'getExceptionNull::Exceptionale()->MaybeegetExceptionNullx=casexofSuccess_->NothingExceptione->Juste{- |
Counterpart to 'either' for 'Either'.
-}switch::(e->b)->(a->b)->Exceptionalea->bswitchfgx=casexofSuccessa->gaExceptione->fe{- |
If you are sure that the value is always a 'Success'
you can tell that the run-time system
thus making your program lazy.
However, try to avoid this function by using 'catch' and friends,
since this function is partial.
-}force::Exceptionalea->Exceptionaleaforce~(Successa)=SuccessamapException::(e0->e1)->Exceptionale0a->Exceptionale1amapExceptionfx=casexofSuccessa->SuccessaExceptione->Exception(fe)mapExceptional::(e0->e1)->(a->b)->Exceptionale0a->Exceptionale1bmapExceptionalfgx=casexofSuccessa->Success(ga)Exceptione->Exception(fe)throw::e->Exceptionaleathrow=Exceptionassert::e->Bool->Exceptionale()asserteb=ifbthenSuccess()elsethrowecatch::Exceptionale0a->(e0->Exceptionale1a)->Exceptionale1acatchxhandler=casexofSuccessa->SuccessaExceptione->handlere{-
bracket ::
Exceptional e h ->
(h -> Exceptional e ()) ->
(h -> Exceptional e a) ->
Exceptional e a
bracket open close action =
open >>= \h ->
case action h of
-}resolve::(e->a)->Exceptionalea->aresolvehandlerx=casexofSuccessa->aExceptione->handlereinstanceFunctor(Exceptionale)wherefmapfx=casexofSuccessa->Success(fa)Exceptione->ExceptioneinstanceApplicative(Exceptionale)wherepure=Successf<*>x=casefofExceptione->ExceptioneSuccessg->casexofSuccessa->Success(ga)Exceptione->ExceptioneinstanceMonad(Exceptionale)wherereturn=Successfail_msg=Exception(error"Exception.Synchronous: Monad.fail method is not supported")x>>=f=casexofExceptione->ExceptioneSuccessy->fyinstanceMonadFix(Exceptionale)wheremfixf=letunSuccess~(Successx)=xa=f(unSuccessa)ina{-
A MonadPlus instance would require another class, say DefaultException,
that provides a default exception used for @mzero@.
In Control.Monad.Error this is handled by the Error class.
Since String is a typical type used for exceptions -
shall there be a DefaultException String instance?
-}-- * Monad transformer-- | like ErrorT, but ExceptionalT is the better name in order to distinguish from real (programming) errorsnewtypeExceptionalTema=ExceptionalT{runExceptionalT::m(Exceptionalea)}fromErrorT::Monadm=>ErrorTema->ExceptionalTemafromErrorT=fromEitherT.runErrorTtoErrorT::Monadm=>ExceptionalTema->ErrorTematoErrorT=ErrorT.toEitherTfromEitherT::Monadm=>m(Eitherea)->ExceptionalTemafromEitherT=ExceptionalT.liftMfromEithertoEitherT::Monadm=>ExceptionalTema->m(Eitherea)toEitherT=liftMtoEither.runExceptionalT{- |
see 'force'
-}forceT::Monadm=>ExceptionalTema->ExceptionalTemaforceT=ExceptionalT.liftMforce.runExceptionalTmapExceptionT::(Monadm)=>(e0->e1)->ExceptionalTe0ma->ExceptionalTe1mamapExceptionTf=ExceptionalT.liftM(mapExceptionf).runExceptionalTmapExceptionalT::(m(Exceptionale0a)->n(Exceptionale1b))->ExceptionalTe0ma->ExceptionalTe1nbmapExceptionalTf=ExceptionalT.f.runExceptionalTthrowT::(Monadm)=>e->ExceptionalTemathrowT=ExceptionalT.return.throwassertT::(Monadm)=>e->Bool->ExceptionalTem()assertTe=ExceptionalT.return.assertecatchT::(Monadm)=>ExceptionalTe0ma->(e0->ExceptionalTe1ma)->ExceptionalTe1macatchTactionhandler=ExceptionalT$runExceptionalTaction>>=\x->casexofSuccessa->return$SuccessaExceptione->runExceptionalT$handlere{- |
If the enclosed monad has custom exception facilities,
they could skip the cleanup code.
Make sure, that this cannot happen by choosing an appropriate monad.
-}bracketT::(Monadm)=>ExceptionalTemh->(h->ExceptionalTem())->(h->ExceptionalTema)->ExceptionalTemabracketTopencloseaction=open>>=\h->ExceptionalT$doa<-runExceptionalT(actionh)c<-runExceptionalT(closeh)return(a>>=\r->c>>returnr)resolveT::(Monadm)=>(e->ma)->ExceptionalTema->maresolveThandlerx=dor<-runExceptionalTxresolvehandler(fmapreturnr)tryT::(Monadm)=>ExceptionalTema->m(Exceptionalea)tryT=runExceptionalT{- |
Repeat an action until an exception occurs.
Initialize the result with @empty@ and add new elements using @cons@
(e.g. @[]@ and @(:)@).
The exception handler decides whether the terminating exception
is re-raised ('Just') or catched ('Nothing').
-}manyT::(Monadm)=>(e0->Maybee1){- ^ exception handler -}->(a->b->b){- ^ @cons@ function -}->b{- ^ @empty@ -}->ExceptionalTe0ma{- ^ atomic action to repeat -}->ExceptionalTe1mbmanyThandlerconsemptyaction=liftM(flipappEndoempty)$manyMonoidThandler$liftM(Endo.cons)actionmanyMonoidT::(Monadm,Monoida)=>(e0->Maybee1){- ^ exception handler -}->ExceptionalTe0ma{- ^ atomic action to repeat -}->ExceptionalTe1mamanyMonoidThandleraction=letrecourse=dor<-lift$tryTactioncaserof-- Exception e -> maybe (return empty) throwT (handler e)-- more lazyExceptione->ExceptionalT$return$maybe(Successmempty)throw(handlere)Successx->liftM(mappendx)recourseinrecourseinstanceFunctorm=>Functor(ExceptionalTem)wherefmapf(ExceptionalTx)=ExceptionalT(fmap(fmapf)x)instanceApplicativem=>Applicative(ExceptionalTem)wherepure=ExceptionalT.pure.pureExceptionalTf<*>ExceptionalTx=ExceptionalT(fmap(<*>)f<*>x)instanceMonadm=>Monad(ExceptionalTem)wherereturn=ExceptionalT.return.returnx0>>=f=ExceptionalT$runExceptionalTx0>>=\x1->casex1ofExceptione->return(Exceptione)Successx->runExceptionalT$fxinstance(MonadFixm)=>MonadFix(ExceptionalTem)wheremfixf=ExceptionalT$mfix$\(Successr)->runExceptionalT$frinstanceMonadTrans(ExceptionalTe)whereliftm=ExceptionalT$liftMSuccessm{-
instance MonadIO m => MonadIO (ExceptionalT e m) where
liftIO act = ExceptionalT $ liftIO $ liftM Success act
-}