automaton-1.5: Effectful streams and automata in coalgebraic encoding
Safe HaskellSafe-Inferred
LanguageHaskell2010

Data.Automaton.Trans.Except

Description

An Automaton in the ExceptT monad can throw an exception to terminate.

This module defines several ways to throw exceptions, and implements control flow by handling them.

The API is heavily inspired by dunai.

Synopsis

Documentation

newtype AutomatonExcept a b m e Source #

An Automaton that can terminate with an exception.

  • m: The monad that the Automaton 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))

Constructors

AutomatonExcept 

Instances

Instances details
MFunctor (AutomatonExcept a b :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Data.Automaton.Trans.Except

Methods

hoist :: forall m n (b0 :: k). Monad m => (forall a0. m a0 -> n a0) -> AutomatonExcept a b m b0 -> AutomatonExcept a b n b0 #

MonadTrans (AutomatonExcept a b) Source # 
Instance details

Defined in Data.Automaton.Trans.Except

Methods

lift :: Monad m => m a0 -> AutomatonExcept a b m a0 #

Monad m => Applicative (AutomatonExcept a b m) Source # 
Instance details

Defined in Data.Automaton.Trans.Except

Methods

pure :: a0 -> AutomatonExcept a b m a0 #

(<*>) :: AutomatonExcept a b m (a0 -> b0) -> AutomatonExcept a b m a0 -> AutomatonExcept a b m b0 #

liftA2 :: (a0 -> b0 -> c) -> AutomatonExcept a b m a0 -> AutomatonExcept a b m b0 -> AutomatonExcept a b m c #

(*>) :: AutomatonExcept a b m a0 -> AutomatonExcept a b m b0 -> AutomatonExcept a b m b0 #

(<*) :: AutomatonExcept a b m a0 -> AutomatonExcept a b m b0 -> AutomatonExcept a b m a0 #

Monad m => Functor (AutomatonExcept a b m) Source # 
Instance details

Defined in Data.Automaton.Trans.Except

Methods

fmap :: (a0 -> b0) -> AutomatonExcept a b m a0 -> AutomatonExcept a b m b0 #

(<$) :: a0 -> AutomatonExcept a b m b0 -> AutomatonExcept a b m a0 #

Monad m => Monad (AutomatonExcept a b m) Source # 
Instance details

Defined in Data.Automaton.Trans.Except

Methods

(>>=) :: AutomatonExcept a b m a0 -> (a0 -> AutomatonExcept a b m b0) -> AutomatonExcept a b m b0 #

(>>) :: AutomatonExcept a b m a0 -> AutomatonExcept a b m b0 -> AutomatonExcept a b m b0 #

return :: a0 -> AutomatonExcept a b m a0 #

Monad m => Selective (AutomatonExcept a b m) Source # 
Instance details

Defined in Data.Automaton.Trans.Except

Methods

select :: AutomatonExcept a b m (Either a0 b0) -> AutomatonExcept a b m (a0 -> b0) -> AutomatonExcept a b m b0 #

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.

forever :: Monad m => AutomatonExcept a b m e -> Automaton m a b Source #

throw :: Monad m => e -> Automaton (ExceptT e m) a b Source #

Immediately throw the given exception.

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.

pass :: Monad m => Automaton (ExceptT e m) a a Source #

Do not throw 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 #

Converts an Automaton in MaybeT to an Automaton in ExceptT.

Whenever Nothing is thrown, throw () instead.

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.

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.

inExceptT :: Monad m => Automaton (ExceptT e m) (ExceptT e m a) a Source #

Embed an ExceptT value inside the Automaton.

Whenever the input value is an ordinary value, it is passed on. If it is an exception, it is raised.

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.

once_ :: Monad m => m e -> AutomatonExcept a b m e Source #

Variant of once without input.

step_ :: Monad m => b -> AutomatonExcept a b m () Source #

Advances a single tick outputting the value, and then throws ().

performOnFirstSample :: Monad m => m (Automaton m a b) -> Automaton m a b Source #

Extract an Automaton from a monadic action.

Runs a monadic action that produces an Automaton on the first step, and then runs result for all further inputs (including the first one).

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 #

Run the first Automaton until the second value in the output tuple is Just c, then start the second automaton, discarding the current output b.

This is analogous to Yampa's switch, with Maybe instead of Event.

dSwitch :: Monad m => Automaton m a (b, Maybe c) -> (c -> Automaton m a b) -> Automaton m a b Source #

Run the first Automaton until the second value in the output tuple is Just c, then start the second automaton one step later (after the current b has been output).

Analog to Yampa's dswitch, with Maybe instead of Event.

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.

Constructors

ExceptT (m (Either e a)) 

Instances

Instances details
MFunctor (ExceptT e :: (Type -> Type) -> Type -> Type) 
Instance details

Defined in Control.Monad.Morph

Methods

hoist :: forall m n (b :: k). Monad m => (forall a. m a -> n a) -> ExceptT e m b -> ExceptT e n b #

MonadSplit g m => MonadSplit g (ExceptT e m) 
Instance details

Defined in Control.Monad.Random.Class

Methods

getSplit :: ExceptT e m g #

Functor m => Generic1 (ExceptT e m :: Type -> Type) 
Instance details

Defined in Control.Monad.Trans.Except

Associated Types

type Rep1 (ExceptT e m) :: k -> Type #

Methods

from1 :: forall (a :: k). ExceptT e m a -> Rep1 (ExceptT e m) a #

to1 :: forall (a :: k). Rep1 (ExceptT e m) a -> ExceptT e m a #

MMonad (ExceptT e) 
Instance details

Defined in Control.Monad.Morph

Methods

embed :: forall (n :: Type -> Type) m b. Monad n => (forall a. m a -> ExceptT e n a) -> ExceptT e m b -> ExceptT e n b #

MonadTrans (ExceptT e) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

lift :: Monad m => m a -> ExceptT e m a #

MonadInterleave m => MonadInterleave (ExceptT e m) 
Instance details

Defined in Control.Monad.Random.Class

Methods

interleave :: ExceptT e m a -> ExceptT e m a #

MonadRandom m => MonadRandom (ExceptT e m) 
Instance details

Defined in Control.Monad.Random.Class

Methods

getRandomR :: Random a => (a, a) -> ExceptT e m a #

getRandom :: Random a => ExceptT e m a #

getRandomRs :: Random a => (a, a) -> ExceptT e m [a] #

getRandoms :: Random a => ExceptT e m [a] #

MonadFail m => MonadFail (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

fail :: String -> ExceptT e m a #

MonadFix m => MonadFix (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

mfix :: (a -> ExceptT e m a) -> ExceptT e m a #

MonadIO m => MonadIO (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

liftIO :: IO a -> ExceptT e m a #

MonadZip m => MonadZip (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

mzip :: ExceptT e m a -> ExceptT e m b -> ExceptT e m (a, b) #

mzipWith :: (a -> b -> c) -> ExceptT e m a -> ExceptT e m b -> ExceptT e m c #

munzip :: ExceptT e m (a, b) -> (ExceptT e m a, ExceptT e m b) #

Foldable f => Foldable (ExceptT e f) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

fold :: Monoid m => ExceptT e f m -> m #

foldMap :: Monoid m => (a -> m) -> ExceptT e f a -> m #

foldMap' :: Monoid m => (a -> m) -> ExceptT e f a -> m #

foldr :: (a -> b -> b) -> b -> ExceptT e f a -> b #

foldr' :: (a -> b -> b) -> b -> ExceptT e f a -> b #

foldl :: (b -> a -> b) -> b -> ExceptT e f a -> b #

foldl' :: (b -> a -> b) -> b -> ExceptT e f a -> b #

foldr1 :: (a -> a -> a) -> ExceptT e f a -> a #

foldl1 :: (a -> a -> a) -> ExceptT e f a -> a #

toList :: ExceptT e f a -> [a] #

null :: ExceptT e f a -> Bool #

length :: ExceptT e f a -> Int #

elem :: Eq a => a -> ExceptT e f a -> Bool #

maximum :: Ord a => ExceptT e f a -> a #

minimum :: Ord a => ExceptT e f a -> a #

sum :: Num a => ExceptT e f a -> a #

product :: Num a => ExceptT e f a -> a #

(Eq e, Eq1 m) => Eq1 (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

liftEq :: (a -> b -> Bool) -> ExceptT e m a -> ExceptT e m b -> Bool #

(Ord e, Ord1 m) => Ord1 (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

liftCompare :: (a -> b -> Ordering) -> ExceptT e m a -> ExceptT e m b -> Ordering #

(Read e, Read1 m) => Read1 (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

liftReadsPrec :: (Int -> ReadS a) -> ReadS [a] -> Int -> ReadS (ExceptT e m a) #

liftReadList :: (Int -> ReadS a) -> ReadS [a] -> ReadS [ExceptT e m a] #

liftReadPrec :: ReadPrec a -> ReadPrec [a] -> ReadPrec (ExceptT e m a) #

liftReadListPrec :: ReadPrec a -> ReadPrec [a] -> ReadPrec [ExceptT e m a] #

(Show e, Show1 m) => Show1 (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

liftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> ExceptT e m a -> ShowS #

liftShowList :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> [ExceptT e m a] -> ShowS #

Contravariant m => Contravariant (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

contramap :: (a' -> a) -> ExceptT e m a -> ExceptT e m a' #

(>$) :: b -> ExceptT e m b -> ExceptT e m a #

Traversable f => Traversable (ExceptT e f) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

traverse :: Applicative f0 => (a -> f0 b) -> ExceptT e f a -> f0 (ExceptT e f b) #

sequenceA :: Applicative f0 => ExceptT e f (f0 a) -> f0 (ExceptT e f a) #

mapM :: Monad m => (a -> m b) -> ExceptT e f a -> m (ExceptT e f b) #

sequence :: Monad m => ExceptT e f (m a) -> m (ExceptT e f a) #

(Functor m, Monad m, Monoid e) => Alternative (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

empty :: ExceptT e m a #

(<|>) :: ExceptT e m a -> ExceptT e m a -> ExceptT e m a #

some :: ExceptT e m a -> ExceptT e m [a] #

many :: ExceptT e m a -> ExceptT e m [a] #

(Functor m, Monad m) => Applicative (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

pure :: a -> ExceptT e m a #

(<*>) :: ExceptT e m (a -> b) -> ExceptT e m a -> ExceptT e m b #

liftA2 :: (a -> b -> c) -> ExceptT e m a -> ExceptT e m b -> ExceptT e m c #

(*>) :: ExceptT e m a -> ExceptT e m b -> ExceptT e m b #

(<*) :: ExceptT e m a -> ExceptT e m b -> ExceptT e m a #

Functor m => Functor (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

fmap :: (a -> b) -> ExceptT e m a -> ExceptT e m b #

(<$) :: a -> ExceptT e m b -> ExceptT e m a #

Monad m => Monad (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

(>>=) :: ExceptT e m a -> (a -> ExceptT e m b) -> ExceptT e m b #

(>>) :: ExceptT e m a -> ExceptT e m b -> ExceptT e m b #

return :: a -> ExceptT e m a #

(Monad m, Monoid e) => MonadPlus (ExceptT e m) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

mzero :: ExceptT e m a #

mplus :: ExceptT e m a -> ExceptT e m a -> ExceptT e m a #

PrimMonad m => PrimMonad (ExceptT e m) 
Instance details

Defined in Control.Monad.Primitive

Associated Types

type PrimState (ExceptT e m) #

Methods

primitive :: (State# (PrimState (ExceptT e m)) -> (# State# (PrimState (ExceptT e m)), a #)) -> ExceptT e m a #

Generic (ExceptT e m a) 
Instance details

Defined in Control.Monad.Trans.Except

Associated Types

type Rep (ExceptT e m a) :: Type -> Type #

Methods

from :: ExceptT e m a -> Rep (ExceptT e m a) x #

to :: Rep (ExceptT e m a) x -> ExceptT e m a #

(Read e, Read1 m, Read a) => Read (ExceptT e m a) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

readsPrec :: Int -> ReadS (ExceptT e m a) #

readList :: ReadS [ExceptT e m a] #

readPrec :: ReadPrec (ExceptT e m a) #

readListPrec :: ReadPrec [ExceptT e m a] #

(Show e, Show1 m, Show a) => Show (ExceptT e m a) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

showsPrec :: Int -> ExceptT e m a -> ShowS #

show :: ExceptT e m a -> String #

showList :: [ExceptT e m a] -> ShowS #

(Eq e, Eq1 m, Eq a) => Eq (ExceptT e m a) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

(==) :: ExceptT e m a -> ExceptT e m a -> Bool #

(/=) :: ExceptT e m a -> ExceptT e m a -> Bool #

(Ord e, Ord1 m, Ord a) => Ord (ExceptT e m a) 
Instance details

Defined in Control.Monad.Trans.Except

Methods

compare :: ExceptT e m a -> ExceptT e m a -> Ordering #

(<) :: ExceptT e m a -> ExceptT e m a -> Bool #

(<=) :: ExceptT e m a -> ExceptT e m a -> Bool #

(>) :: ExceptT e m a -> ExceptT e m a -> Bool #

(>=) :: ExceptT e m a -> ExceptT e m a -> Bool #

max :: ExceptT e m a -> ExceptT e m a -> ExceptT e m a #

min :: ExceptT e m a -> ExceptT e m a -> ExceptT e m a #

type Rep1 (ExceptT e m :: Type -> Type) 
Instance details

Defined in Control.Monad.Trans.Except

type Rep1 (ExceptT e m :: Type -> Type) = D1 ('MetaData "ExceptT" "Control.Monad.Trans.Except" "transformers-0.6.1.0" 'True) (C1 ('MetaCons "ExceptT" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (m :.: Rec1 (Either e))))
type PrimState (ExceptT e m) 
Instance details

Defined in Control.Monad.Primitive

type PrimState (ExceptT e m) = PrimState m
type Rep (ExceptT e m a) 
Instance details

Defined in Control.Monad.Trans.Except

type Rep (ExceptT e m a) = D1 ('MetaData "ExceptT" "Control.Monad.Trans.Except" "transformers-0.6.1.0" 'True) (C1 ('MetaCons "ExceptT" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (m (Either e a)))))

throwE :: forall (m :: Type -> Type) e a. Monad m => e -> ExceptT e m a #

Signal an exception value e.

runExceptT :: ExceptT e m a -> m (Either e a) #

The inverse of ExceptT.