monad-fault-0.1.0.0

Safe HaskellNone
LanguageHaskell2010

Control.Monad.Trans.Fault

Contents

Description

monad-fault provides an extensible way to trigger arbitrary failures within your programs. Potential faults are tracked in the type system via MonadFault constraints. Useful for testing fault tolerance (e.g. retry logic)

For instance:

mightFault :: (MonadIO m, MonadFaults '["redis", "s3", "sqs"] m)
           => Redis.Connection
           -> AWS.Env
           -> m ()
mightFault redisConn awsEnv = do
  s3Result <- faulty "s3" $ readFromS3 awsEnv
  faulty "redis" $ writeToRedis redisConn s3Result
  faulty @"sqs" $ writeToSQS awsEnv s3Result

In production, we'd run this with FaultlessT which will cause the faults to be elided:

runFaultlessT (mightFault redisConn awsEnv) :: IO ()

But while testing, we can create a FaultController and run with FaultyT. In our tests, we can set different parts of the program to fault using setFault & resetFault. We can then confirm, for instance, that a Redis blip followed by a retry still results in the correct effects being performed on the world:

fc <- newFaultController '["redis", "s3", "sqs"]
setFault "redis" fc (redisException "uh oh!")
runFaultyT fc (mightFault redisConn awsEnv) shouldThrow anyException
resetFault @"redis" fc
runFaultyT fc (mightFault redisConn awsEnv)
performChecks

Synopsis

Causing faults

fault :: forall fault m. MonadFault fault m => m () Source #

Cause a fault named fault

Meant to be used w/TypeApplications

>>> fault @"redis" :: MonadFault "redis" m => m ()

faulty :: forall fault m a. MonadFault fault m => m a -> m a Source #

Tag an action as a potential fault named fault

>>> faulty @"failure name" $ mightFail

The MonadFault Class

class Monad m => MonadFault fault m where Source #

m is capable of having fault. faults are named with type-level strings

Minimal complete definition

faultPrx

Methods

faultPrx :: Proxy fault -> m () Source #

Cause a fault named fault

Instances

Monad m => MonadFault fault (FaultlessT m) Source # 

Methods

faultPrx :: Proxy Symbol fault -> FaultlessT m () Source #

(Monad (t m), MonadTrans t, MonadFault fault m) => MonadFault fault (t m) Source #

Automatic instances for MonadTrans

Methods

faultPrx :: Proxy Symbol fault -> t m () Source #

(MonadIO m, HasFault f faults) => MonadFault f (FaultyT faults m) Source # 

Methods

faultPrx :: Proxy Symbol f -> FaultyT faults m () Source #

type family MonadFaults (faults :: [Symbol]) (m :: * -> *) :: Constraint where ... Source #

One m frequently has many potential faults

Equations

MonadFaults '[] m = () 
MonadFaults (fault ': rest) m = (MonadFault fault m, MonadFaults rest m) 

The FaultlessT & FaultyT monad transformers

newtype FaultlessT m a Source #

Can never fault.

Constructors

FaultlessT 

Fields

Instances

MonadTrans FaultlessT Source # 

Methods

lift :: Monad m => m a -> FaultlessT m a #

MonadTransControl FaultlessT Source # 

Associated Types

type StT (FaultlessT :: (* -> *) -> * -> *) a :: * #

Methods

liftWith :: Monad m => (Run FaultlessT -> m a) -> FaultlessT m a #

restoreT :: Monad m => m (StT FaultlessT a) -> FaultlessT m a #

MonadBaseControl b m => MonadBaseControl b (FaultlessT m) Source # 

Associated Types

type StM (FaultlessT m :: * -> *) a :: * #

Methods

liftBaseWith :: (RunInBase (FaultlessT m) b -> b a) -> FaultlessT m a #

restoreM :: StM (FaultlessT m) a -> FaultlessT m a #

MonadBase b m => MonadBase b (FaultlessT m) Source # 

Methods

liftBase :: b α -> FaultlessT m α #

MonadError e m => MonadError e (FaultlessT m) Source # 

Methods

throwError :: e -> FaultlessT m a #

catchError :: FaultlessT m a -> (e -> FaultlessT m a) -> FaultlessT m a #

MonadReader r m => MonadReader r (FaultlessT m) Source # 

Methods

ask :: FaultlessT m r #

local :: (r -> r) -> FaultlessT m a -> FaultlessT m a #

reader :: (r -> a) -> FaultlessT m a #

MonadState s m => MonadState s (FaultlessT m) Source # 

Methods

get :: FaultlessT m s #

put :: s -> FaultlessT m () #

state :: (s -> (a, s)) -> FaultlessT m a #

Monad m => MonadFault fault (FaultlessT m) Source # 

Methods

faultPrx :: Proxy Symbol fault -> FaultlessT m () Source #

Monad m => Monad (FaultlessT m) Source # 

Methods

(>>=) :: FaultlessT m a -> (a -> FaultlessT m b) -> FaultlessT m b #

(>>) :: FaultlessT m a -> FaultlessT m b -> FaultlessT m b #

return :: a -> FaultlessT m a #

fail :: String -> FaultlessT m a #

Functor m => Functor (FaultlessT m) Source # 

Methods

fmap :: (a -> b) -> FaultlessT m a -> FaultlessT m b #

(<$) :: a -> FaultlessT m b -> FaultlessT m a #

Applicative m => Applicative (FaultlessT m) Source # 

Methods

pure :: a -> FaultlessT m a #

(<*>) :: FaultlessT m (a -> b) -> FaultlessT m a -> FaultlessT m b #

(*>) :: FaultlessT m a -> FaultlessT m b -> FaultlessT m b #

(<*) :: FaultlessT m a -> FaultlessT m b -> FaultlessT m a #

MonadIO m => MonadIO (FaultlessT m) Source # 

Methods

liftIO :: IO a -> FaultlessT m a #

MonadThrow m => MonadThrow (FaultlessT m) Source # 

Methods

throwM :: Exception e => e -> FaultlessT m a #

MonadCatch m => MonadCatch (FaultlessT m) Source # 

Methods

catch :: Exception e => FaultlessT m a -> (e -> FaultlessT m a) -> FaultlessT m a #

MonadLogger m => MonadLogger (FaultlessT m) Source # 

Methods

monadLoggerLog :: ToLogStr msg => Loc -> LogSource -> LogLevel -> msg -> FaultlessT m () #

MonadResource m => MonadResource (FaultlessT m) Source # 
type StT FaultlessT a Source # 
type StM (FaultlessT m) a Source # 

runFaultlessT :: FaultlessT m a -> m a Source #

Unwrap FaultlessT, ignoring all possible faults

newtype FaultyT faults m a Source #

Monad transformer that allows the caller to control which faults occur

Constructors

FaultyT 

Fields

Instances

MonadBaseControl b m => MonadBaseControl b (FaultyT faults m) Source # 

Associated Types

type StM (FaultyT faults m :: * -> *) a :: * #

Methods

liftBaseWith :: (RunInBase (FaultyT faults m) b -> b a) -> FaultyT faults m a #

restoreM :: StM (FaultyT faults m) a -> FaultyT faults m a #

MonadBase b m => MonadBase b (FaultyT faults m) Source # 

Methods

liftBase :: b α -> FaultyT faults m α #

MonadError e m => MonadError e (FaultyT faults m) Source # 

Methods

throwError :: e -> FaultyT faults m a #

catchError :: FaultyT faults m a -> (e -> FaultyT faults m a) -> FaultyT faults m a #

MonadReader r m => MonadReader r (FaultyT faults m) Source #

Even though FaultyT has a ReaderT within, our MonadReader instance is just a lift

Methods

ask :: FaultyT faults m r #

local :: (r -> r) -> FaultyT faults m a -> FaultyT faults m a #

reader :: (r -> a) -> FaultyT faults m a #

MonadState s m => MonadState s (FaultyT faults m) Source # 

Methods

get :: FaultyT faults m s #

put :: s -> FaultyT faults m () #

state :: (s -> (a, s)) -> FaultyT faults m a #

(MonadIO m, HasFault f faults) => MonadFault f (FaultyT faults m) Source # 

Methods

faultPrx :: Proxy Symbol f -> FaultyT faults m () Source #

MonadTrans (FaultyT faults) Source # 

Methods

lift :: Monad m => m a -> FaultyT faults m a #

MonadTransControl (FaultyT faults) Source # 

Associated Types

type StT (FaultyT faults :: (* -> *) -> * -> *) a :: * #

Methods

liftWith :: Monad m => (Run (FaultyT faults) -> m a) -> FaultyT faults m a #

restoreT :: Monad m => m (StT (FaultyT faults) a) -> FaultyT faults m a #

Monad m => Monad (FaultyT faults m) Source # 

Methods

(>>=) :: FaultyT faults m a -> (a -> FaultyT faults m b) -> FaultyT faults m b #

(>>) :: FaultyT faults m a -> FaultyT faults m b -> FaultyT faults m b #

return :: a -> FaultyT faults m a #

fail :: String -> FaultyT faults m a #

Functor m => Functor (FaultyT faults m) Source # 

Methods

fmap :: (a -> b) -> FaultyT faults m a -> FaultyT faults m b #

(<$) :: a -> FaultyT faults m b -> FaultyT faults m a #

Applicative m => Applicative (FaultyT faults m) Source # 

Methods

pure :: a -> FaultyT faults m a #

(<*>) :: FaultyT faults m (a -> b) -> FaultyT faults m a -> FaultyT faults m b #

(*>) :: FaultyT faults m a -> FaultyT faults m b -> FaultyT faults m b #

(<*) :: FaultyT faults m a -> FaultyT faults m b -> FaultyT faults m a #

MonadIO m => MonadIO (FaultyT faults m) Source # 

Methods

liftIO :: IO a -> FaultyT faults m a #

MonadThrow m => MonadThrow (FaultyT faults m) Source # 

Methods

throwM :: Exception e => e -> FaultyT faults m a #

MonadCatch m => MonadCatch (FaultyT faults m) Source # 

Methods

catch :: Exception e => FaultyT faults m a -> (e -> FaultyT faults m a) -> FaultyT faults m a #

MonadLogger m => MonadLogger (FaultyT faults m) Source # 

Methods

monadLoggerLog :: ToLogStr msg => Loc -> LogSource -> LogLevel -> msg -> FaultyT faults m () #

MonadResource m => MonadResource (FaultyT faults m) Source # 

Methods

liftResourceT :: ResourceT IO a -> FaultyT faults m a #

type StT (FaultyT faults) a Source # 
type StT (FaultyT faults) a = StT (ReaderT * (FaultController faults)) a
type StM (FaultyT faults m) a Source # 
type StM (FaultyT faults m) a = ComposeSt (FaultyT faults) m a

runFaultyT :: FaultController faults -> FaultyT faults m a -> m a Source #

Run a FaultyT, causing faults along the way according to the given FaultController

Controlling faults

newFaultController :: NewFault faults => IO (FaultController faults) Source #

Create a FaultController initially configured to never fault

setFault :: forall fault faults e. (HasFault fault faults, Exception e) => e -> FaultController faults -> IO () Source #

Set a fault to throw a given Exception

resetFault :: forall fault faults e. (HasFault fault faults, Exception e) => FaultController faults -> IO () Source #

Set a fault to not fail.

Debugging

printFaultController :: FaultController faults -> IO () Source #

>>> fc <- newFaultController @'["x", "y"]
>>> printFaultController fc
(FCCons @"x" FaultConfig Nothing (FCCons @"y" FaultConfig Nothing FCNil))

askFaultController :: Monad m => FaultyT faults m (FaultController faults) Source #

Access the FaultController within a FaultyT. You usually can't use this because you'll write your programs in terms of MonadFault "whatever" m =>

Internals

data FaultController faults where Source #

Extensible record of FaultConfigs, each tagged with a fault name at the type level

Constructors

FCNil :: FaultController '[] 
FCCons :: forall f rest. KnownSymbol f => Proxy f -> !(IORef FaultConfig) -> FaultController rest -> FaultController (f ': rest) 

data FaultConfig Source #

If the exception is Just, we fault. Otherwise, we don't

class NewFault faults where Source #

Create a default, non-faulting FaultController

Minimal complete definition

newFaultController

Methods

newFaultController :: IO (FaultController faults) Source #

Create a FaultController initially configured to never fault

class HasFault f faults where Source #

Query & modify a FaultController at certain faults

Minimal complete definition

getFaultConfig, setFaultConfig

Instances

HasFault goal rest => HasFault goal ((:) Symbol f rest) Source # 
HasFault goal ((:) Symbol goal rest) Source #