Safe Haskell | None |
---|---|
Language | Haskell2010 |
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
"redis" $ writeToRedis redisConn s3Resultfaulty
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"]
"redis" fc (redisException "uh oh!")setFault
runFaultyT
fc (mightFault redisConn awsEnv)shouldThrow
anyExceptionresetFault
@"redis" fcrunFaultyT
fc (mightFault redisConn awsEnv) performChecks
- fault :: forall fault m. MonadFault fault m => m ()
- faulty :: forall fault m a. MonadFault fault m => m a -> m a
- class Monad m => MonadFault fault m where
- type family MonadFaults (faults :: [Symbol]) (m :: * -> *) :: Constraint where ...
- newtype FaultlessT m a = FaultlessT {
- unFaultlessT :: IdentityT m a
- runFaultlessT :: FaultlessT m a -> m a
- newtype FaultyT faults m a = FaultyT {
- unFaultyT :: ReaderT (FaultController faults) m a
- runFaultyT :: FaultController faults -> FaultyT faults m a -> m a
- newFaultController :: NewFault faults => IO (FaultController faults)
- setFault :: forall fault faults e. (HasFault fault faults, Exception e) => e -> FaultController faults -> IO ()
- resetFault :: forall fault faults e. (HasFault fault faults, Exception e) => FaultController faults -> IO ()
- printFaultController :: FaultController faults -> IO ()
- showFaultController :: FaultController faults -> IO String
- askFaultController :: Monad m => FaultyT faults m (FaultController faults)
- data FaultController faults where
- FCNil :: FaultController '[]
- FCCons :: forall f rest. KnownSymbol f => Proxy f -> !(IORef FaultConfig) -> FaultController rest -> FaultController (f ': rest)
- data FaultConfig = FaultConfig (Maybe SomeException)
- class NewFault faults where
- class HasFault f faults where
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
. fault
s are
named with type-level strings
Monad m => MonadFault fault (FaultlessT m) Source # | |
(Monad (t m), MonadTrans t, MonadFault fault m) => MonadFault fault (t m) Source # | Automatic instances for MonadTrans |
(MonadIO m, HasFault f faults) => MonadFault f (FaultyT faults m) Source # | |
type family MonadFaults (faults :: [Symbol]) (m :: * -> *) :: Constraint where ... Source #
One m
frequently has many potential faults
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.
FaultlessT | |
|
MonadTrans FaultlessT Source # | |
MonadTransControl FaultlessT Source # | |
MonadBaseControl b m => MonadBaseControl b (FaultlessT m) Source # | |
MonadBase b m => MonadBase b (FaultlessT m) Source # | |
MonadError e m => MonadError e (FaultlessT m) Source # | |
MonadReader r m => MonadReader r (FaultlessT m) Source # | |
MonadState s m => MonadState s (FaultlessT m) Source # | |
Monad m => MonadFault fault (FaultlessT m) Source # | |
Monad m => Monad (FaultlessT m) Source # | |
Functor m => Functor (FaultlessT m) Source # | |
Applicative m => Applicative (FaultlessT m) Source # | |
MonadIO m => MonadIO (FaultlessT m) Source # | |
MonadThrow m => MonadThrow (FaultlessT m) Source # | |
MonadCatch m => MonadCatch (FaultlessT m) Source # | |
MonadLogger m => MonadLogger (FaultlessT m) Source # | |
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
FaultyT | |
|
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))
showFaultController :: FaultController faults -> IO String Source #
Create the output of printFaultController
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
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
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
getFaultConfig :: FaultController faults -> IO FaultConfig Source #
setFaultConfig :: FaultConfig -> FaultController faults -> IO () Source #