| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
Data.Automaton.Trans.Except
Description
Synopsis
- newtype AutomatonExcept a b m e = AutomatonExcept {
- getAutomatonExcept :: StreamExcept b (ReaderT a m) e
- tagged :: Monad m => Automaton (ExceptT e1 m) a b -> Automaton (ExceptT e2 m) (a, e2) b
- forever :: Monad m => AutomatonExcept a b m e -> Automaton m a b
- throw :: Monad m => e -> Automaton (ExceptT e m) a b
- try :: Monad m => Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
- step :: Monad m => (a -> m (b, e)) -> AutomatonExcept a b m e
- pass :: Monad m => Automaton (ExceptT e m) a a
- safe :: Monad m => Automaton m a b -> AutomatonExcept a b m e
- safely :: Monad m => AutomatonExcept a b m Void -> Automaton m a b
- throwOnCond :: Monad m => (a -> Bool) -> e -> Automaton (ExceptT e m) a a
- maybeToExceptS :: (Functor m, Monad m) => Automaton (MaybeT m) a b -> Automaton (ExceptT () m) a b
- exceptS :: (Functor m, Monad m) => Automaton (ExceptT e m) a b -> Automaton m a (Either e b)
- listToAutomatonExcept :: Monad m => [b] -> AutomatonExcept a b m ()
- reactimateExcept :: Monad m => AutomatonExcept () () m e -> m e
- runAutomatonExcept :: Monad m => AutomatonExcept a b m e -> Automaton (ExceptT e m) a b
- throwS :: Monad m => Automaton (ExceptT e m) e a
- throwOnCondM :: Monad m => (a -> m Bool) -> e -> Automaton (ExceptT e m) a a
- throwOn :: Monad m => e -> Automaton (ExceptT e m) Bool ()
- throwOn' :: Monad m => Automaton (ExceptT e m) (Bool, e) ()
- throwOnMaybe :: Monad m => (a -> Maybe e) -> Automaton (ExceptT e m) a a
- throwMaybe :: Monad m => Automaton (ExceptT e m) (Maybe e) (Maybe void)
- catchS :: Monad m => Automaton (ExceptT e m) a b -> (e -> Automaton m a b) -> Automaton m a b
- untilE :: Monad m => Automaton m a b -> Automaton m b (Maybe e) -> Automaton (ExceptT e m) a b
- inExceptT :: Monad m => Automaton (ExceptT e m) (ExceptT e m a) a
- currentInput :: Monad m => AutomatonExcept e b m e
- once :: Monad m => (a -> m e) -> AutomatonExcept a b m e
- once_ :: Monad m => m e -> AutomatonExcept a b m e
- step_ :: Monad m => b -> AutomatonExcept a b m ()
- performOnFirstSample :: Monad m => m (Automaton m a b) -> Automaton m a b
- reactimateB :: Monad m => Automaton m () Bool -> m ()
- switch :: Monad m => Automaton m a (b, Maybe c) -> (c -> Automaton m a b) -> Automaton m a b
- dSwitch :: Monad m => Automaton m a (b, Maybe c) -> (c -> Automaton m a b) -> Automaton m a b
- newtype ExceptT e (m :: Type -> Type) a = ExceptT (m (Either e a))
- throwE :: forall (m :: Type -> Type) e a. Monad m => e -> ExceptT e m a
- runExceptT :: ExceptT e m a -> m (Either e a)
Documentation
newtype AutomatonExcept a b m e Source #
An Automaton that can terminate with an exception.
m: The monad that theAutomatonmay take side effects in.a: The type of input values the stream constantly consumes.b: The type of output values the stream constantly produces.e: The type of exceptions with which the stream can terminate.
This type is useful because it is a monad in the exception type e.
returncorresponds to throwing an exception immediately.>>=is exception handling: The first value throws an exception, while the Kleisli arrow handles the exception and produces a new signal function, which can throw exceptions in a different type.
Consider this example: @ automaton :: AutomatonExcept a b m e1 f :: e1 -> AutomatonExcept a b m e2
example :: AutomatonExcept a b m e2 example = automaton >>= f @
Here, automaton produces output values of type b until an exception e1 occurs.
The function f is called on the exception value and produces a continuation automaton
which is then executed (until it possibly throws an exception e2 itself).
Note: By "exceptions", we mean an ExceptT transformer layer, not IO exceptions.
do-Notation
Since the type has a Monad instance, you can use do notation to define exception handling:
example :: Monad m => Automaton m a Int example = safely $ do try $ count >>> throwOnMaybe (n -> guard (n < 10)) safe $ arr $ const 0
Here, a counter is incremented until it reaches 10. Once it does, an exception is thrown, and the automaton continues to output 0 forever.
Performance of Monad vs. Applicative
The generality of the monad interface comes at a cost, though.
In order to achieve higher performance, you should use the Monad interface sparingly.
Whenever you can express the same control flow using Functor, Applicative, Selective,
or just the (>>) operator, you should do this.
The encoding of the internal state type will be much more efficiently optimized.
The reason for this is that in an expression ma >>= f,
the type of f is e1 -> AutomatonExcept a b m e2,
which implies that the state of the AutomatonExcept produced isn't known at compile time,
and thus GHC cannot optimize the automaton.
But often the full expressiveness of >>= isn't necessary, and in these cases,
a much faster automaton is produced by using Functor, Applicative and Selective.
Recursive definitions when not using the full Monad interface
When the optimized interface is used (by avoiding >>=),
the same caveat as in Data.Stream regarding recursive definitions applies:
They will loop at runtime.
For example, one might expect that this automaton would repeatedly output numbers below or equal to 10:
bad :: Monad m => Automaton m a Int bad = safely $ do try $ count >>> throwOnMaybe (n -> guard (n > 10)) safe bad
But in fact it will loop, and never produce output.
This is because the do notation desugars to an expression involving >>, not >>=,
which has higher performance in principle, but doesn't support recursion.
Using the full monad interface misses out on some optimizations, but works:
userSawtooth :: Int -> Automaton IO a Int
userSawtooth nMax = safely $ do
try $ count >>> throwOnMaybe (n -> guard (n > nMax))
nMax' <- once_ $ do
putStrLn "Maximum reached, please enter next nMax:"
readLn
safe $ userSawtooth nMax'
The reason this do notation is desugared using >>= is because the variable nMax' is used later.
To define a recursive, optimized, exception handling automaton, use forever:
sawtooth :: Automaton IO a Int sawtooth = forever $ try $ count >>> throwOnMaybe (n -> guard (n > 10))
Constructors
| AutomatonExcept | |
Fields
| |
Instances
tagged :: Monad m => Automaton (ExceptT e1 m) a b -> Automaton (ExceptT e2 m) (a, e2) b Source #
In case an exception occurs in the first argument, replace the exception by the second component of the tuple.
try :: Monad m => Automaton (ExceptT e m) a b -> AutomatonExcept a b m e Source #
Execute an Automaton in ExceptT until it raises an exception.
Typically used to enter the monad context of AutomatonExcept.
step :: Monad m => (a -> m (b, e)) -> AutomatonExcept a b m e Source #
Advances a single tick with the given Kleisli arrow, and then throws an exception.
safe :: Monad m => Automaton m a b -> AutomatonExcept a b m e Source #
An Automaton without an ExceptT layer never throws an exception, and can
thus have an arbitrary exception type.
In particular, the exception type can be Void, so it can be used as the last statement in an AutomatonExcept do-block.
See safely for an example.
safely :: Monad m => AutomatonExcept a b m Void -> Automaton m a b Source #
If no exception can occur, the Automaton can be executed without the ExceptT
layer.
Used to exit the AutomatonExcept context, often in combination with safe:
automaton = safely $ do e <- try someAutomaton once $ input -> putStrLn $ "Whoops, something happened when receiving input " ++ show input ++ ": " ++ show e ++ ", but I'll continue now." safe fallbackAutomaton
throwOnCond :: Monad m => (a -> Bool) -> e -> Automaton (ExceptT e m) a a Source #
Throw the exception e whenever the function evaluates to True.
maybeToExceptS :: (Functor m, Monad m) => Automaton (MaybeT m) a b -> Automaton (ExceptT () m) a b Source #
exceptS :: (Functor m, Monad m) => Automaton (ExceptT e m) a b -> Automaton m a (Either e b) Source #
Escape an ExceptT layer by outputting the exception whenever it occurs.
If an exception occurs, the current state is is tested again on the next input.
listToAutomatonExcept :: Monad m => [b] -> AutomatonExcept a b m () Source #
Converts a list to an AutomatonExcept, which outputs an element of the list at
each step, throwing () when the list ends.
reactimateExcept :: Monad m => AutomatonExcept () () m e -> m e Source #
reactimates an AutomatonExcept until it throws an exception.
runAutomatonExcept :: Monad m => AutomatonExcept a b m e -> Automaton (ExceptT e m) a b Source #
throwS :: Monad m => Automaton (ExceptT e m) e a Source #
Immediately throw the incoming exception.
This is useful to combine with ArrowChoice,
e.g. with if and case expressions in Arrow syntax.
throwOnCondM :: Monad m => (a -> m Bool) -> e -> Automaton (ExceptT e m) a a Source #
Throws the exception when the input is True.
Variant of throwOnCond for Kleisli arrows.
throwOn :: Monad m => e -> Automaton (ExceptT e m) Bool () Source #
Throw the exception when the input is True.
throwOn' :: Monad m => Automaton (ExceptT e m) (Bool, e) () Source #
Variant of throwOn, where the exception may change every tick.
throwOnMaybe :: Monad m => (a -> Maybe e) -> Automaton (ExceptT e m) a a Source #
When the predicate evaluates to Just e, throw the exception e, otherwise forward the input.
throwMaybe :: Monad m => Automaton (ExceptT e m) (Maybe e) (Maybe void) Source #
When the input is Just e, throw the exception e.
This does not output any data since it terminates on the first nontrivial input.
catchS :: Monad m => Automaton (ExceptT e m) a b -> (e -> Automaton m a b) -> Automaton m a b Source #
Catch an exception in an Automaton.
As soon as an exception occurs, switch to a new Automaton,
the exception handler, based on the exception value.
For exception catching where the handler can throw further exceptions, see AutomatonExcept further below.
untilE :: Monad m => Automaton m a b -> Automaton m b (Maybe e) -> Automaton (ExceptT e m) a b Source #
Similar to Yampa's delayed switching. Loses a b in case of an exception.
currentInput :: Monad m => AutomatonExcept e b m e Source #
Immediately throw the current input as an exception.
Useful inside AutomatonExcept if you don't want to advance a further step in execution,
but first see what the current input is before continuing.
once :: Monad m => (a -> m e) -> AutomatonExcept a b m e Source #
Inside the AutomatonExcept monad, execute an action of the wrapped monad.
This passes the last input value to the action, but doesn't advance a tick.
step_ :: Monad m => b -> AutomatonExcept a b m () Source #
Advances a single tick outputting the value, and then throws ().
reactimateB :: Monad m => Automaton m () Bool -> m () Source #
reactimates an Automaton until it returns True.
switch :: Monad m => Automaton m a (b, Maybe c) -> (c -> Automaton m a b) -> Automaton m a b Source #
dSwitch :: Monad m => Automaton m a (b, Maybe c) -> (c -> Automaton m a b) -> Automaton m a b Source #
newtype ExceptT e (m :: Type -> Type) a #
A monad transformer that adds exceptions to other monads.
ExceptT constructs a monad parameterized over two things:
- e - The exception type.
- m - The inner monad.
The return function yields a computation that produces the given
value, while >>= sequences two subcomputations, exiting on the
first exception.
Instances
runExceptT :: ExceptT e m a -> m (Either e a) #
The inverse of ExceptT.