Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
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 theAutomaton
may 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
.
return
corresponds 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))
AutomatonExcept | |
|
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 #
reactimate
s 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 #
reactimate
s 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
.