{-# LANGUAGE Arrows #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE StrictData #-}

{- | 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@.
-}
module Data.Automaton.Trans.Except (
  module Data.Automaton.Trans.Except,
  module Control.Monad.Trans.Except,
)
where

-- base
import Control.Arrow (arr, returnA, (<<<), (>>>))
import Control.Category qualified as Category
import Data.Void (Void, absurd)

-- transformers
import Control.Monad.Trans.Except (ExceptT (..), runExceptT, throwE)
import Control.Monad.Trans.Maybe (MaybeT, runMaybeT)
import Control.Monad.Trans.Reader

-- selective
import Control.Selective (Selective)

-- mmorph
import Control.Monad.Morph

-- automaton
import Data.Automaton (
  Automaton (..),
  arrM,
  constM,
  count,
  feedback,
  hoistS,
  liftS,
  mapMaybeS,
  reactimate,
 )
import Data.Automaton.Trans.Except.Internal
import Data.Stream.Except hiding (safely)
import Data.Stream.Except qualified as StreamExcept
import Data.Stream.Optimized (mapOptimizedStreamT)
import Data.Stream.Optimized qualified as StreamOptimized

-- * Throwing exceptions

-- | Throw the exception 'e' whenever the function evaluates to 'True'.
throwOnCond :: (Monad m) => (a -> Bool) -> e -> Automaton (ExceptT e m) a a
throwOnCond :: forall (m :: Type -> Type) a e.
Monad m =>
(a -> Bool) -> e -> Automaton (ExceptT e m) a a
throwOnCond a -> Bool
cond e
e = proc a
a ->
  if a -> Bool
cond a
a
    then Automaton (ExceptT e m) e a
forall (m :: Type -> Type) e a.
Monad m =>
Automaton (ExceptT e m) e a
throwS -< e
e
    else Automaton (ExceptT e m) a a
forall (a :: Type -> Type -> Type) b. Arrow a => a b b
returnA -< a
a

{- | Throws the exception when the input is 'True'.

Variant of 'throwOnCond' for Kleisli arrows.
-}
throwOnCondM :: (Monad m) => (a -> m Bool) -> e -> Automaton (ExceptT e m) a a
throwOnCondM :: forall (m :: Type -> Type) a e.
Monad m =>
(a -> m Bool) -> e -> Automaton (ExceptT e m) a a
throwOnCondM a -> m Bool
cond e
e = proc a
a -> do
  Bool
b <- (a -> ExceptT e m Bool) -> Automaton (ExceptT e m) a Bool
forall (m :: Type -> Type) a b.
Functor m =>
(a -> m b) -> Automaton m a b
arrM (m Bool -> ExceptT e m Bool
forall (m :: Type -> Type) a. Monad m => m a -> ExceptT e m a
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m Bool -> ExceptT e m Bool)
-> (a -> m Bool) -> a -> ExceptT e m Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> m Bool
cond) -< a
a
  if Bool
b
    then Automaton (ExceptT e m) e a
forall (m :: Type -> Type) e a.
Monad m =>
Automaton (ExceptT e m) e a
throwS -< e
e
    else Automaton (ExceptT e m) a a
forall (a :: Type -> Type -> Type) b. Arrow a => a b b
returnA -< a
a

-- | Throw the exception when the input is 'True'.
throwOn :: (Monad m) => e -> Automaton (ExceptT e m) Bool ()
throwOn :: forall (m :: Type -> Type) e.
Monad m =>
e -> Automaton (ExceptT e m) Bool ()
throwOn e
e = proc Bool
b -> Automaton (ExceptT e m) (Bool, e) ()
forall (m :: Type -> Type) e.
Monad m =>
Automaton (ExceptT e m) (Bool, e) ()
throwOn' -< (Bool
b, e
e)

-- | Variant of 'throwOn', where the exception may change every tick.
throwOn' :: (Monad m) => Automaton (ExceptT e m) (Bool, e) ()
throwOn' :: forall (m :: Type -> Type) e.
Monad m =>
Automaton (ExceptT e m) (Bool, e) ()
throwOn' = proc (Bool
b, e
e) ->
  if Bool
b
    then Automaton (ExceptT e m) e ()
forall (m :: Type -> Type) e a.
Monad m =>
Automaton (ExceptT e m) e a
throwS -< e
e
    else Automaton (ExceptT e m) () ()
forall (a :: Type -> Type -> Type) b. Arrow a => a b b
returnA -< ()

{- | When the input is @Just e@, throw the exception @e@.

This does not output any data since it terminates on the first nontrivial input.
-}
throwMaybe :: (Monad m) => Automaton (ExceptT e m) (Maybe e) (Maybe void)
throwMaybe :: forall (m :: Type -> Type) e void.
Monad m =>
Automaton (ExceptT e m) (Maybe e) (Maybe void)
throwMaybe = Automaton (ExceptT e m) e void
-> Automaton (ExceptT e m) (Maybe e) (Maybe void)
forall (m :: Type -> Type) a b.
Monad m =>
Automaton m a b -> Automaton m (Maybe a) (Maybe b)
mapMaybeS Automaton (ExceptT e m) e void
forall (m :: Type -> Type) e a.
Monad m =>
Automaton (ExceptT e m) e a
throwS

{- | Immediately throw the incoming exception.

This is useful to combine with 'ArrowChoice',
e.g. with @if@ and @case@ expressions in Arrow syntax.
-}
throwS :: (Monad m) => Automaton (ExceptT e m) e a
throwS :: forall (m :: Type -> Type) e a.
Monad m =>
Automaton (ExceptT e m) e a
throwS = (e -> ExceptT e m a) -> Automaton (ExceptT e m) e a
forall (m :: Type -> Type) a b.
Functor m =>
(a -> m b) -> Automaton m a b
arrM e -> ExceptT e m a
forall (m :: Type -> Type) e a. Monad m => e -> ExceptT e m a
throwE

-- | Immediately throw the given exception.
throw :: (Monad m) => e -> Automaton (ExceptT e m) a b
throw :: forall (m :: Type -> Type) e a b.
Monad m =>
e -> Automaton (ExceptT e m) a b
throw = ExceptT e m b -> Automaton (ExceptT e m) a b
forall (m :: Type -> Type) b a. Functor m => m b -> Automaton m a b
constM (ExceptT e m b -> Automaton (ExceptT e m) a b)
-> (e -> ExceptT e m b) -> e -> Automaton (ExceptT e m) a b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> ExceptT e m b
forall (m :: Type -> Type) e a. Monad m => e -> ExceptT e m a
throwE

-- | Do not throw an exception.
pass :: (Monad m) => Automaton (ExceptT e m) a a
pass :: forall (m :: Type -> Type) e a.
Monad m =>
Automaton (ExceptT e m) a a
pass = Automaton (ExceptT e m) a a
forall a. Automaton (ExceptT e m) a a
forall {k} (cat :: k -> k -> Type) (a :: k).
Category cat =>
cat a a
Category.id

{- | Converts an 'Automaton' in 'MaybeT' to an 'Automaton' in 'ExceptT'.

Whenever 'Nothing' is thrown, throw @()@ instead.
-}
maybeToExceptS ::
  (Functor m, Monad m) =>
  Automaton (MaybeT m) a b ->
  Automaton (ExceptT () m) a b
maybeToExceptS :: forall (m :: Type -> Type) a b.
(Functor m, Monad m) =>
Automaton (MaybeT m) a b -> Automaton (ExceptT () m) a b
maybeToExceptS = (forall x. MaybeT m x -> ExceptT () m x)
-> Automaton (MaybeT m) a b -> Automaton (ExceptT () m) a b
forall (m :: Type -> Type) (n :: Type -> Type) a b.
Monad m =>
(forall x. m x -> n x) -> Automaton m a b -> Automaton n a b
hoistS (m (Either () x) -> ExceptT () m x
forall e (m :: Type -> Type) a. m (Either e a) -> ExceptT e m a
ExceptT (m (Either () x) -> ExceptT () m x)
-> (MaybeT m x -> m (Either () x)) -> MaybeT m x -> ExceptT () m x
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Either () x -> (x -> Either () x) -> Maybe x -> Either () x
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (() -> Either () x
forall a b. a -> Either a b
Left ()) x -> Either () x
forall a b. b -> Either a b
Right (Maybe x -> Either () x) -> m (Maybe x) -> m (Either () x)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$>) (m (Maybe x) -> m (Either () x))
-> (MaybeT m x -> m (Maybe x)) -> MaybeT m x -> m (Either () x)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. MaybeT m x -> m (Maybe x)
forall (m :: Type -> Type) a. MaybeT m a -> m (Maybe a)
runMaybeT)

-- * Catching exceptions

{- | 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.
-}
catchS :: (Monad m) => Automaton (ExceptT e m) a b -> (e -> Automaton m a b) -> Automaton m a b
catchS :: forall (m :: Type -> Type) e a b.
Monad m =>
Automaton (ExceptT e m) a b
-> (e -> Automaton m a b) -> Automaton m a b
catchS Automaton (ExceptT e m) a b
automaton e -> Automaton m a b
f = AutomatonExcept a b m Void -> Automaton m a b
forall (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m Void -> Automaton m a b
safely (AutomatonExcept a b m Void -> Automaton m a b)
-> AutomatonExcept a b m Void -> Automaton m a b
forall a b. (a -> b) -> a -> b
$ do
  e
e <- Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
forall (m :: Type -> Type) e a b.
Monad m =>
Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
try Automaton (ExceptT e m) a b
automaton
  Automaton m a b -> AutomatonExcept a b m Void
forall (m :: Type -> Type) a b e.
Monad m =>
Automaton m a b -> AutomatonExcept a b m e
safe (Automaton m a b -> AutomatonExcept a b m Void)
-> Automaton m a b -> AutomatonExcept a b m Void
forall a b. (a -> b) -> a -> b
$ e -> Automaton m a b
f e
e

-- | Similar to Yampa's delayed switching. Loses a @b@ in case of an exception.
untilE ::
  (Monad m) =>
  Automaton m a b ->
  Automaton m b (Maybe e) ->
  Automaton (ExceptT e m) a b
untilE :: forall (m :: Type -> Type) a b e.
Monad m =>
Automaton m a b
-> Automaton m b (Maybe e) -> Automaton (ExceptT e m) a b
untilE Automaton m a b
automaton Automaton m b (Maybe e)
automatone = proc a
a -> do
  b
b <- Automaton m a b -> Automaton (ExceptT e m) a b
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a
       b.
(MonadTrans t, Monad m, Functor (t m)) =>
Automaton m a b -> Automaton (t m) a b
liftS Automaton m a b
automaton -< a
a
  Maybe e
me <- Automaton m b (Maybe e) -> Automaton (ExceptT e m) b (Maybe e)
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a
       b.
(MonadTrans t, Monad m, Functor (t m)) =>
Automaton m a b -> Automaton (t m) a b
liftS Automaton m b (Maybe e)
automatone -< b
b
  Automaton (ExceptT e m) (ExceptT e m b) b
forall (m :: Type -> Type) e a.
Monad m =>
Automaton (ExceptT e m) (ExceptT e m a) a
inExceptT -< m (Either e b) -> ExceptT e m b
forall e (m :: Type -> Type) a. m (Either e a) -> ExceptT e m a
ExceptT (m (Either e b) -> ExceptT e m b)
-> m (Either e b) -> ExceptT e m b
forall a b. (a -> b) -> a -> b
$ Either e b -> m (Either e b)
forall a. a -> m a
forall (m :: Type -> Type) a. Monad m => a -> m a
return (Either e b -> m (Either e b)) -> Either e b -> m (Either e b)
forall a b. (a -> b) -> a -> b
$ Either e b -> (e -> Either e b) -> Maybe e -> Either e b
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (b -> Either e b
forall a b. b -> Either a b
Right b
b) e -> Either e b
forall a b. a -> Either a b
Left Maybe e
me

{- | 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.
-}
exceptS :: (Functor m, Monad m) => Automaton (ExceptT e m) a b -> Automaton m a (Either e b)
exceptS :: forall (m :: Type -> Type) e a b.
(Functor m, Monad m) =>
Automaton (ExceptT e m) a b -> Automaton m a (Either e b)
exceptS = OptimizedStreamT (ReaderT a m) (Either e b)
-> Automaton m a (Either e b)
forall (m :: Type -> Type) a b.
OptimizedStreamT (ReaderT a m) b -> Automaton m a b
Automaton (OptimizedStreamT (ReaderT a m) (Either e b)
 -> Automaton m a (Either e b))
-> (Automaton (ExceptT e m) a b
    -> OptimizedStreamT (ReaderT a m) (Either e b))
-> Automaton (ExceptT e m) a b
-> Automaton m a (Either e b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. OptimizedStreamT (ExceptT e (ReaderT a m)) b
-> OptimizedStreamT (ReaderT a m) (Either e b)
forall (m :: Type -> Type) e b.
Monad m =>
OptimizedStreamT (ExceptT e m) b -> OptimizedStreamT m (Either e b)
StreamOptimized.exceptS (OptimizedStreamT (ExceptT e (ReaderT a m)) b
 -> OptimizedStreamT (ReaderT a m) (Either e b))
-> (Automaton (ExceptT e m) a b
    -> OptimizedStreamT (ExceptT e (ReaderT a m)) b)
-> Automaton (ExceptT e m) a b
-> OptimizedStreamT (ReaderT a m) (Either e b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall s.
 ReaderT a (ExceptT e m) (Result s b)
 -> ExceptT e (ReaderT a m) (Result s b))
-> OptimizedStreamT (ReaderT a (ExceptT e m)) b
-> OptimizedStreamT (ExceptT e (ReaderT a m)) b
forall (m :: Type -> Type) (n :: Type -> Type) a b.
(Functor m, Functor n) =>
(forall s. m (Result s a) -> n (Result s b))
-> OptimizedStreamT m a -> OptimizedStreamT n b
mapOptimizedStreamT ReaderT a (ExceptT e m) (Result s b)
-> ExceptT e (ReaderT a m) (Result s b)
forall s.
ReaderT a (ExceptT e m) (Result s b)
-> ExceptT e (ReaderT a m) (Result s b)
forall r e (m :: Type -> Type) a.
ReaderT r (ExceptT e m) a -> ExceptT e (ReaderT r m) a
commuteReader (OptimizedStreamT (ReaderT a (ExceptT e m)) b
 -> OptimizedStreamT (ExceptT e (ReaderT a m)) b)
-> (Automaton (ExceptT e m) a b
    -> OptimizedStreamT (ReaderT a (ExceptT e m)) b)
-> Automaton (ExceptT e m) a b
-> OptimizedStreamT (ExceptT e (ReaderT a m)) b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Automaton (ExceptT e m) a b
-> OptimizedStreamT (ReaderT a (ExceptT e m)) b
forall (m :: Type -> Type) a b.
Automaton m a b -> OptimizedStreamT (ReaderT a m) b
getAutomaton

{- | 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.
-}
inExceptT :: (Monad m) => Automaton (ExceptT e m) (ExceptT e m a) a
inExceptT :: forall (m :: Type -> Type) e a.
Monad m =>
Automaton (ExceptT e m) (ExceptT e m a) a
inExceptT = (ExceptT e m a -> ExceptT e m a)
-> Automaton (ExceptT e m) (ExceptT e m a) a
forall (m :: Type -> Type) a b.
Functor m =>
(a -> m b) -> Automaton m a b
arrM ExceptT e m a -> ExceptT e m a
forall a. a -> a
id

{- | In case an exception occurs in the first argument, replace the exception
by the second component of the tuple.
-}
tagged :: (Monad m) => Automaton (ExceptT e1 m) a b -> Automaton (ExceptT e2 m) (a, e2) b
tagged :: forall (m :: Type -> Type) e1 a b e2.
Monad m =>
Automaton (ExceptT e1 m) a b -> Automaton (ExceptT e2 m) (a, e2) b
tagged Automaton (ExceptT e1 m) a b
automaton = AutomatonExcept (a, e2) b m e2
-> Automaton (ExceptT e2 m) (a, e2) b
forall (m :: Type -> Type) a b e.
Monad m =>
AutomatonExcept a b m e -> Automaton (ExceptT e m) a b
runAutomatonExcept (AutomatonExcept (a, e2) b m e2
 -> Automaton (ExceptT e2 m) (a, e2) b)
-> AutomatonExcept (a, e2) b m e2
-> Automaton (ExceptT e2 m) (a, e2) b
forall a b. (a -> b) -> a -> b
$ Automaton (ExceptT e1 m) (a, e2) b
-> AutomatonExcept (a, e2) b m e1
forall (m :: Type -> Type) e a b.
Monad m =>
Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
try (Automaton (ExceptT e1 m) a b
automaton Automaton (ExceptT e1 m) a b
-> Automaton (ExceptT e1 m) (a, e2) a
-> Automaton (ExceptT e1 m) (a, e2) b
forall {k} (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
<<< ((a, e2) -> a) -> Automaton (ExceptT e1 m) (a, e2) a
forall b c. (b -> c) -> Automaton (ExceptT e1 m) b c
forall (a :: Type -> Type -> Type) b c.
Arrow a =>
(b -> c) -> a b c
arr (a, e2) -> a
forall a b. (a, b) -> a
fst) AutomatonExcept (a, e2) b m e1
-> AutomatonExcept (a, e2) b m e2 -> AutomatonExcept (a, e2) b m e2
forall a b.
AutomatonExcept (a, e2) b m a
-> AutomatonExcept (a, e2) b m b -> AutomatonExcept (a, e2) b m b
forall (f :: Type -> Type) a b. Applicative f => f a -> f b -> f b
*> ((a, e2) -> e2
forall a b. (a, b) -> b
snd ((a, e2) -> e2)
-> AutomatonExcept (a, e2) b m (a, e2)
-> AutomatonExcept (a, e2) b m e2
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> AutomatonExcept (a, e2) b m (a, e2)
forall (m :: Type -> Type) e b. Monad m => AutomatonExcept e b m e
currentInput)

-- * Monad interface for Exception Automatons

{- | 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).

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'.

Note: By "exceptions", we mean an 'ExceptT' transformer layer, not 'IO' exceptions.
-}
newtype AutomatonExcept a b m e = AutomatonExcept {forall a b (m :: Type -> Type) e.
AutomatonExcept a b m e -> StreamExcept b (ReaderT a m) e
getAutomatonExcept :: StreamExcept b (ReaderT a m) e}
  deriving newtype ((forall a b.
 (a -> b) -> AutomatonExcept a b m a -> AutomatonExcept a b m b)
-> (forall a b.
    a -> AutomatonExcept a b m b -> AutomatonExcept a b m a)
-> Functor (AutomatonExcept a b m)
forall a b. a -> AutomatonExcept a b m b -> AutomatonExcept a b m a
forall a b.
(a -> b) -> AutomatonExcept a b m a -> AutomatonExcept a b m b
forall a b (m :: Type -> Type) a b.
Monad m =>
a -> AutomatonExcept a b m b -> AutomatonExcept a b m a
forall a b (m :: Type -> Type) a b.
Monad m =>
(a -> b) -> AutomatonExcept a b m a -> AutomatonExcept a b m b
forall (f :: Type -> Type).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall a b (m :: Type -> Type) a b.
Monad m =>
(a -> b) -> AutomatonExcept a b m a -> AutomatonExcept a b m b
fmap :: forall a b.
(a -> b) -> AutomatonExcept a b m a -> AutomatonExcept a b m b
$c<$ :: forall a b (m :: Type -> Type) a b.
Monad m =>
a -> AutomatonExcept a b m b -> AutomatonExcept a b m a
<$ :: forall a b. a -> AutomatonExcept a b m b -> AutomatonExcept a b m a
Functor, Functor (AutomatonExcept a b m)
Functor (AutomatonExcept a b m) =>
(forall a. a -> AutomatonExcept a b m a)
-> (forall a b.
    AutomatonExcept a b m (a -> b)
    -> AutomatonExcept a b m a -> AutomatonExcept a b m b)
-> (forall a b c.
    (a -> b -> c)
    -> AutomatonExcept a b m a
    -> AutomatonExcept a b m b
    -> AutomatonExcept a b m c)
-> (forall a b.
    AutomatonExcept a b m a
    -> AutomatonExcept a b m b -> AutomatonExcept a b m b)
-> (forall a b.
    AutomatonExcept a b m a
    -> AutomatonExcept a b m b -> AutomatonExcept a b m a)
-> Applicative (AutomatonExcept a b m)
forall a. a -> AutomatonExcept a b m a
forall a b.
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m a
forall a b.
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m b
forall a b.
AutomatonExcept a b m (a -> b)
-> AutomatonExcept a b m a -> AutomatonExcept a b m b
forall a b c.
(a -> b -> c)
-> AutomatonExcept a b m a
-> AutomatonExcept a b m b
-> AutomatonExcept a b m c
forall a b (m :: Type -> Type).
Monad m =>
Functor (AutomatonExcept a b m)
forall a b (m :: Type -> Type) a.
Monad m =>
a -> AutomatonExcept a b m a
forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m a
forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m b
forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m (a -> b)
-> AutomatonExcept a b m a -> AutomatonExcept a b m b
forall a b (m :: Type -> Type) a b c.
Monad m =>
(a -> b -> c)
-> AutomatonExcept a b m a
-> AutomatonExcept a b m b
-> AutomatonExcept a b m c
forall (f :: Type -> Type).
Functor f =>
(forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
$cpure :: forall a b (m :: Type -> Type) a.
Monad m =>
a -> AutomatonExcept a b m a
pure :: forall a. a -> AutomatonExcept a b m a
$c<*> :: forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m (a -> b)
-> AutomatonExcept a b m a -> AutomatonExcept a b m b
<*> :: forall a b.
AutomatonExcept a b m (a -> b)
-> AutomatonExcept a b m a -> AutomatonExcept a b m b
$cliftA2 :: forall a b (m :: Type -> Type) a b c.
Monad m =>
(a -> b -> c)
-> AutomatonExcept a b m a
-> AutomatonExcept a b m b
-> AutomatonExcept a b m c
liftA2 :: forall a b c.
(a -> b -> c)
-> AutomatonExcept a b m a
-> AutomatonExcept a b m b
-> AutomatonExcept a b m c
$c*> :: forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m b
*> :: forall a b.
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m b
$c<* :: forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m a
<* :: forall a b.
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m a
Applicative, Applicative (AutomatonExcept a b m)
Applicative (AutomatonExcept a b m) =>
(forall a b.
 AutomatonExcept a b m (Either a b)
 -> AutomatonExcept a b m (a -> b) -> AutomatonExcept a b m b)
-> Selective (AutomatonExcept a b m)
forall a b.
AutomatonExcept a b m (Either a b)
-> AutomatonExcept a b m (a -> b) -> AutomatonExcept a b m b
forall a b (m :: Type -> Type).
Monad m =>
Applicative (AutomatonExcept a b m)
forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m (Either a b)
-> AutomatonExcept a b m (a -> b) -> AutomatonExcept a b m b
forall (f :: Type -> Type).
Applicative f =>
(forall a b. f (Either a b) -> f (a -> b) -> f b) -> Selective f
$cselect :: forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m (Either a b)
-> AutomatonExcept a b m (a -> b) -> AutomatonExcept a b m b
select :: forall a b.
AutomatonExcept a b m (Either a b)
-> AutomatonExcept a b m (a -> b) -> AutomatonExcept a b m b
Selective, Applicative (AutomatonExcept a b m)
Applicative (AutomatonExcept a b m) =>
(forall a b.
 AutomatonExcept a b m a
 -> (a -> AutomatonExcept a b m b) -> AutomatonExcept a b m b)
-> (forall a b.
    AutomatonExcept a b m a
    -> AutomatonExcept a b m b -> AutomatonExcept a b m b)
-> (forall a. a -> AutomatonExcept a b m a)
-> Monad (AutomatonExcept a b m)
forall a. a -> AutomatonExcept a b m a
forall a b.
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m b
forall a b.
AutomatonExcept a b m a
-> (a -> AutomatonExcept a b m b) -> AutomatonExcept a b m b
forall a b (m :: Type -> Type).
Monad m =>
Applicative (AutomatonExcept a b m)
forall a b (m :: Type -> Type) a.
Monad m =>
a -> AutomatonExcept a b m a
forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m b
forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m a
-> (a -> AutomatonExcept a b m b) -> AutomatonExcept a b m b
forall (m :: Type -> Type).
Applicative m =>
(forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
$c>>= :: forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m a
-> (a -> AutomatonExcept a b m b) -> AutomatonExcept a b m b
>>= :: forall a b.
AutomatonExcept a b m a
-> (a -> AutomatonExcept a b m b) -> AutomatonExcept a b m b
$c>> :: forall a b (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m b
>> :: forall a b.
AutomatonExcept a b m a
-> AutomatonExcept a b m b -> AutomatonExcept a b m b
$creturn :: forall a b (m :: Type -> Type) a.
Monad m =>
a -> AutomatonExcept a b m a
return :: forall a. a -> AutomatonExcept a b m a
Monad)

instance MonadTrans (AutomatonExcept a b) where
  lift :: forall (m :: Type -> Type) a.
Monad m =>
m a -> AutomatonExcept a b m a
lift = StreamExcept b (ReaderT a m) a -> AutomatonExcept a b m a
forall a b (m :: Type -> Type) e.
StreamExcept b (ReaderT a m) e -> AutomatonExcept a b m e
AutomatonExcept (StreamExcept b (ReaderT a m) a -> AutomatonExcept a b m a)
-> (m a -> StreamExcept b (ReaderT a m) a)
-> m a
-> AutomatonExcept a b m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ReaderT a m a -> StreamExcept b (ReaderT a m) a
forall (m :: Type -> Type) a. Monad m => m a -> StreamExcept b m a
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (ReaderT a m a -> StreamExcept b (ReaderT a m) a)
-> (m a -> ReaderT a m a) -> m a -> StreamExcept b (ReaderT a m) a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m a -> ReaderT a m a
forall (m :: Type -> Type) a. Monad m => m a -> ReaderT a m a
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift

instance MFunctor (AutomatonExcept a b) where
  hoist :: forall (m :: Type -> Type) (n :: Type -> Type) b.
Monad m =>
(forall a. m a -> n a)
-> AutomatonExcept a b m b -> AutomatonExcept a b n b
hoist forall a. m a -> n a
morph = StreamExcept b (ReaderT a n) b -> AutomatonExcept a b n b
forall a b (m :: Type -> Type) e.
StreamExcept b (ReaderT a m) e -> AutomatonExcept a b m e
AutomatonExcept (StreamExcept b (ReaderT a n) b -> AutomatonExcept a b n b)
-> (AutomatonExcept a b m b -> StreamExcept b (ReaderT a n) b)
-> AutomatonExcept a b m b
-> AutomatonExcept a b n b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. ReaderT a m a -> ReaderT a n a)
-> StreamExcept b (ReaderT a m) b -> StreamExcept b (ReaderT a n) b
forall {k} (t :: (Type -> Type) -> k -> Type) (m :: Type -> Type)
       (n :: Type -> Type) (b :: k).
(MFunctor t, Monad m) =>
(forall a. m a -> n a) -> t m b -> t n b
forall (m :: Type -> Type) (n :: Type -> Type) b.
Monad m =>
(forall a. m a -> n a) -> StreamExcept b m b -> StreamExcept b n b
hoist ((m a -> n a) -> ReaderT a m a -> ReaderT a n a
forall (m :: Type -> Type) a (n :: Type -> Type) b r.
(m a -> n b) -> ReaderT r m a -> ReaderT r n b
mapReaderT m a -> n a
forall a. m a -> n a
morph) (StreamExcept b (ReaderT a m) b -> StreamExcept b (ReaderT a n) b)
-> (AutomatonExcept a b m b -> StreamExcept b (ReaderT a m) b)
-> AutomatonExcept a b m b
-> StreamExcept b (ReaderT a n) b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AutomatonExcept a b m b -> StreamExcept b (ReaderT a m) b
forall a b (m :: Type -> Type) e.
AutomatonExcept a b m e -> StreamExcept b (ReaderT a m) e
getAutomatonExcept

runAutomatonExcept :: (Monad m) => AutomatonExcept a b m e -> Automaton (ExceptT e m) a b
runAutomatonExcept :: forall (m :: Type -> Type) a b e.
Monad m =>
AutomatonExcept a b m e -> Automaton (ExceptT e m) a b
runAutomatonExcept = OptimizedStreamT (ReaderT a (ExceptT e m)) b
-> Automaton (ExceptT e m) a b
forall (m :: Type -> Type) a b.
OptimizedStreamT (ReaderT a m) b -> Automaton m a b
Automaton (OptimizedStreamT (ReaderT a (ExceptT e m)) b
 -> Automaton (ExceptT e m) a b)
-> (AutomatonExcept a b m e
    -> OptimizedStreamT (ReaderT a (ExceptT e m)) b)
-> AutomatonExcept a b m e
-> Automaton (ExceptT e m) a b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. ExceptT e (ReaderT a m) a -> ReaderT a (ExceptT e m) a)
-> OptimizedStreamT (ExceptT e (ReaderT a m)) b
-> OptimizedStreamT (ReaderT a (ExceptT e m)) b
forall {k} (t :: (Type -> Type) -> k -> Type) (m :: Type -> Type)
       (n :: Type -> Type) (b :: k).
(MFunctor t, Monad m) =>
(forall a. m a -> n a) -> t m b -> t n b
forall (m :: Type -> Type) (n :: Type -> Type) b.
Monad m =>
(forall a. m a -> n a)
-> OptimizedStreamT m b -> OptimizedStreamT n b
hoist ExceptT e (ReaderT a m) a -> ReaderT a (ExceptT e m) a
forall a. ExceptT e (ReaderT a m) a -> ReaderT a (ExceptT e m) a
forall e r (m :: Type -> Type) a.
ExceptT e (ReaderT r m) a -> ReaderT r (ExceptT e m) a
commuteReaderBack (OptimizedStreamT (ExceptT e (ReaderT a m)) b
 -> OptimizedStreamT (ReaderT a (ExceptT e m)) b)
-> (AutomatonExcept a b m e
    -> OptimizedStreamT (ExceptT e (ReaderT a m)) b)
-> AutomatonExcept a b m e
-> OptimizedStreamT (ReaderT a (ExceptT e m)) b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StreamExcept b (ReaderT a m) e
-> OptimizedStreamT (ExceptT e (ReaderT a m)) b
forall a (m :: Type -> Type) e.
StreamExcept a m e -> OptimizedStreamT (ExceptT e m) a
runStreamExcept (StreamExcept b (ReaderT a m) e
 -> OptimizedStreamT (ExceptT e (ReaderT a m)) b)
-> (AutomatonExcept a b m e -> StreamExcept b (ReaderT a m) e)
-> AutomatonExcept a b m e
-> OptimizedStreamT (ExceptT e (ReaderT a m)) b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AutomatonExcept a b m e -> StreamExcept b (ReaderT a m) e
forall a b (m :: Type -> Type) e.
AutomatonExcept a b m e -> StreamExcept b (ReaderT a m) e
getAutomatonExcept

{- | Execute an 'Automaton' in 'ExceptT' until it raises an exception.

Typically used to enter the monad context of 'AutomatonExcept'.
-}
try :: (Monad m) => Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
try :: forall (m :: Type -> Type) e a b.
Monad m =>
Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
try = StreamExcept b (ReaderT a m) e -> AutomatonExcept a b m e
forall a b (m :: Type -> Type) e.
StreamExcept b (ReaderT a m) e -> AutomatonExcept a b m e
AutomatonExcept (StreamExcept b (ReaderT a m) e -> AutomatonExcept a b m e)
-> (Automaton (ExceptT e m) a b -> StreamExcept b (ReaderT a m) e)
-> Automaton (ExceptT e m) a b
-> AutomatonExcept a b m e
forall b c a. (b -> c) -> (a -> b) -> a -> c
. OptimizedStreamT (ExceptT e (ReaderT a m)) b
-> StreamExcept b (ReaderT a m) e
forall a (m :: Type -> Type) e.
OptimizedStreamT (ExceptT e m) a -> StreamExcept a m e
InitialExcept (OptimizedStreamT (ExceptT e (ReaderT a m)) b
 -> StreamExcept b (ReaderT a m) e)
-> (Automaton (ExceptT e m) a b
    -> OptimizedStreamT (ExceptT e (ReaderT a m)) b)
-> Automaton (ExceptT e m) a b
-> StreamExcept b (ReaderT a m) e
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. ReaderT a (ExceptT e m) a -> ExceptT e (ReaderT a m) a)
-> OptimizedStreamT (ReaderT a (ExceptT e m)) b
-> OptimizedStreamT (ExceptT e (ReaderT a m)) b
forall {k} (t :: (Type -> Type) -> k -> Type) (m :: Type -> Type)
       (n :: Type -> Type) (b :: k).
(MFunctor t, Monad m) =>
(forall a. m a -> n a) -> t m b -> t n b
forall (m :: Type -> Type) (n :: Type -> Type) b.
Monad m =>
(forall a. m a -> n a)
-> OptimizedStreamT m b -> OptimizedStreamT n b
hoist ReaderT a (ExceptT e m) a -> ExceptT e (ReaderT a m) a
forall a. ReaderT a (ExceptT e m) a -> ExceptT e (ReaderT a m) a
forall r e (m :: Type -> Type) a.
ReaderT r (ExceptT e m) a -> ExceptT e (ReaderT r m) a
commuteReader (OptimizedStreamT (ReaderT a (ExceptT e m)) b
 -> OptimizedStreamT (ExceptT e (ReaderT a m)) b)
-> (Automaton (ExceptT e m) a b
    -> OptimizedStreamT (ReaderT a (ExceptT e m)) b)
-> Automaton (ExceptT e m) a b
-> OptimizedStreamT (ExceptT e (ReaderT a m)) b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Automaton (ExceptT e m) a b
-> OptimizedStreamT (ReaderT a (ExceptT e m)) b
forall (m :: Type -> Type) a b.
Automaton m a b -> OptimizedStreamT (ReaderT a m) b
getAutomaton

{- | 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.
-}
currentInput :: (Monad m) => AutomatonExcept e b m e
currentInput :: forall (m :: Type -> Type) e b. Monad m => AutomatonExcept e b m e
currentInput = Automaton (ExceptT e m) e b -> AutomatonExcept e b m e
forall (m :: Type -> Type) e a b.
Monad m =>
Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
try Automaton (ExceptT e m) e b
forall (m :: Type -> Type) e a.
Monad m =>
Automaton (ExceptT e m) e a
throwS

{- | 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
@
-}
safely :: (Monad m) => AutomatonExcept a b m Void -> Automaton m a b
safely :: forall (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m Void -> Automaton m a b
safely = OptimizedStreamT (ReaderT a m) b -> Automaton m a b
forall (m :: Type -> Type) a b.
OptimizedStreamT (ReaderT a m) b -> Automaton m a b
Automaton (OptimizedStreamT (ReaderT a m) b -> Automaton m a b)
-> (AutomatonExcept a b m Void -> OptimizedStreamT (ReaderT a m) b)
-> AutomatonExcept a b m Void
-> Automaton m a b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StreamExcept b (ReaderT a m) Void
-> OptimizedStreamT (ReaderT a m) b
forall (m :: Type -> Type) a.
Monad m =>
StreamExcept a m Void -> OptimizedStreamT m a
StreamExcept.safely (StreamExcept b (ReaderT a m) Void
 -> OptimizedStreamT (ReaderT a m) b)
-> (AutomatonExcept a b m Void
    -> StreamExcept b (ReaderT a m) Void)
-> AutomatonExcept a b m Void
-> OptimizedStreamT (ReaderT a m) b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AutomatonExcept a b m Void -> StreamExcept b (ReaderT a m) Void
forall a b (m :: Type -> Type) e.
AutomatonExcept a b m e -> StreamExcept b (ReaderT a m) e
getAutomatonExcept

{- | 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.
-}
safe :: (Monad m) => Automaton m a b -> AutomatonExcept a b m e
safe :: forall (m :: Type -> Type) a b e.
Monad m =>
Automaton m a b -> AutomatonExcept a b m e
safe = Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
forall (m :: Type -> Type) e a b.
Monad m =>
Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
try (Automaton (ExceptT e m) a b -> AutomatonExcept a b m e)
-> (Automaton m a b -> Automaton (ExceptT e m) a b)
-> Automaton m a b
-> AutomatonExcept a b m e
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Automaton m a b -> Automaton (ExceptT e m) a b
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a
       b.
(MonadTrans t, Monad m, Functor (t m)) =>
Automaton m a b -> Automaton (t m) a b
liftS

{- | 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) => (a -> m e) -> AutomatonExcept a b m e
once :: forall (m :: Type -> Type) a e b.
Monad m =>
(a -> m e) -> AutomatonExcept a b m e
once a -> m e
f = StreamExcept b (ReaderT a m) e -> AutomatonExcept a b m e
forall a b (m :: Type -> Type) e.
StreamExcept b (ReaderT a m) e -> AutomatonExcept a b m e
AutomatonExcept (StreamExcept b (ReaderT a m) e -> AutomatonExcept a b m e)
-> StreamExcept b (ReaderT a m) e -> AutomatonExcept a b m e
forall a b. (a -> b) -> a -> b
$ OptimizedStreamT (ExceptT e (ReaderT a m)) b
-> StreamExcept b (ReaderT a m) e
forall a (m :: Type -> Type) e.
OptimizedStreamT (ExceptT e m) a -> StreamExcept a m e
InitialExcept (OptimizedStreamT (ExceptT e (ReaderT a m)) b
 -> StreamExcept b (ReaderT a m) e)
-> OptimizedStreamT (ExceptT e (ReaderT a m)) b
-> StreamExcept b (ReaderT a m) e
forall a b. (a -> b) -> a -> b
$ ExceptT e (ReaderT a m) b
-> OptimizedStreamT (ExceptT e (ReaderT a m)) b
forall (m :: Type -> Type) a. m a -> OptimizedStreamT m a
StreamOptimized.constM (ExceptT e (ReaderT a m) b
 -> OptimizedStreamT (ExceptT e (ReaderT a m)) b)
-> ExceptT e (ReaderT a m) b
-> OptimizedStreamT (ExceptT e (ReaderT a m)) b
forall a b. (a -> b) -> a -> b
$ ReaderT a m (Either e b) -> ExceptT e (ReaderT a m) b
forall e (m :: Type -> Type) a. m (Either e a) -> ExceptT e m a
ExceptT (ReaderT a m (Either e b) -> ExceptT e (ReaderT a m) b)
-> ReaderT a m (Either e b) -> ExceptT e (ReaderT a m) b
forall a b. (a -> b) -> a -> b
$ (a -> m (Either e b)) -> ReaderT a m (Either e b)
forall r (m :: Type -> Type) a. (r -> m a) -> ReaderT r m a
ReaderT ((a -> m (Either e b)) -> ReaderT a m (Either e b))
-> (a -> m (Either e b)) -> ReaderT a m (Either e b)
forall a b. (a -> b) -> a -> b
$ (e -> Either e b) -> m e -> m (Either e b)
forall a b. (a -> b) -> m a -> m b
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
fmap e -> Either e b
forall a b. a -> Either a b
Left (m e -> m (Either e b)) -> (a -> m e) -> a -> m (Either e b)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> m e
f

-- | Variant of 'once' without input.
once_ :: (Monad m) => m e -> AutomatonExcept a b m e
once_ :: forall (m :: Type -> Type) e a b.
Monad m =>
m e -> AutomatonExcept a b m e
once_ = (a -> m e) -> AutomatonExcept a b m e
forall (m :: Type -> Type) a e b.
Monad m =>
(a -> m e) -> AutomatonExcept a b m e
once ((a -> m e) -> AutomatonExcept a b m e)
-> (m e -> a -> m e) -> m e -> AutomatonExcept a b m e
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m e -> a -> m e
forall a b. a -> b -> a
const

-- | Advances a single tick with the given Kleisli arrow, and then throws an exception.
step :: (Monad m) => (a -> m (b, e)) -> AutomatonExcept a b m e
step :: forall (m :: Type -> Type) a b e.
Monad m =>
(a -> m (b, e)) -> AutomatonExcept a b m e
step a -> m (b, e)
f = Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
forall (m :: Type -> Type) e a b.
Monad m =>
Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
try (Automaton (ExceptT e m) a b -> AutomatonExcept a b m e)
-> Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
forall a b. (a -> b) -> a -> b
$ proc a
a -> do
  Int
n <- Automaton (ExceptT e m) () Int
forall n (m :: Type -> Type) a. (Num n, Monad m) => Automaton m a n
count -< ()
  (b
b, e
e) <- (a -> ExceptT e m (b, e)) -> Automaton (ExceptT e m) a (b, e)
forall (m :: Type -> Type) a b.
Functor m =>
(a -> m b) -> Automaton m a b
arrM (m (b, e) -> ExceptT e m (b, e)
forall (m :: Type -> Type) a. Monad m => m a -> ExceptT e m a
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (b, e) -> ExceptT e m (b, e))
-> (a -> m (b, e)) -> a -> ExceptT e m (b, e)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> m (b, e)
f) -< a
a
  ()
_ <- Automaton (ExceptT e m) (Bool, e) ()
forall (m :: Type -> Type) e.
Monad m =>
Automaton (ExceptT e m) (Bool, e) ()
throwOn' -< (Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> (Int
1 :: Int), e
e)
  Automaton (ExceptT e m) b b
forall (a :: Type -> Type -> Type) b. Arrow a => a b b
returnA -< b
b

-- | Advances a single tick outputting the value, and then throws '()'.
step_ :: (Monad m) => b -> AutomatonExcept a b m ()
step_ :: forall (m :: Type -> Type) b a.
Monad m =>
b -> AutomatonExcept a b m ()
step_ b
b = (a -> m (b, ())) -> AutomatonExcept a b m ()
forall (m :: Type -> Type) a b e.
Monad m =>
(a -> m (b, e)) -> AutomatonExcept a b m e
step ((a -> m (b, ())) -> AutomatonExcept a b m ())
-> (a -> m (b, ())) -> AutomatonExcept a b m ()
forall a b. (a -> b) -> a -> b
$ m (b, ()) -> a -> m (b, ())
forall a b. a -> b -> a
const (m (b, ()) -> a -> m (b, ())) -> m (b, ()) -> a -> m (b, ())
forall a b. (a -> b) -> a -> b
$ (b, ()) -> m (b, ())
forall a. a -> m a
forall (m :: Type -> Type) a. Monad m => a -> m a
return (b
b, ())

{- | Converts a list to an 'AutomatonExcept', which outputs an element of the list at
each step, throwing '()' when the list ends.
-}
listToAutomatonExcept :: (Monad m) => [b] -> AutomatonExcept a b m ()
listToAutomatonExcept :: forall (m :: Type -> Type) b a.
Monad m =>
[b] -> AutomatonExcept a b m ()
listToAutomatonExcept = (b -> AutomatonExcept a b m ()) -> [b] -> AutomatonExcept a b m ()
forall (t :: Type -> Type) (m :: Type -> Type) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ b -> AutomatonExcept a b m ()
forall (m :: Type -> Type) b a.
Monad m =>
b -> AutomatonExcept a b m ()
step_

-- * Utilities definable in terms of 'AutomatonExcept'

{- | 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).
-}
performOnFirstSample :: (Monad m) => m (Automaton m a b) -> Automaton m a b
performOnFirstSample :: forall (m :: Type -> Type) a b.
Monad m =>
m (Automaton m a b) -> Automaton m a b
performOnFirstSample m (Automaton m a b)
mAutomaton = AutomatonExcept a b m Void -> Automaton m a b
forall (m :: Type -> Type) a b.
Monad m =>
AutomatonExcept a b m Void -> Automaton m a b
safely (AutomatonExcept a b m Void -> Automaton m a b)
-> AutomatonExcept a b m Void -> Automaton m a b
forall a b. (a -> b) -> a -> b
$ do
  Automaton m a b
automaton <- m (Automaton m a b) -> AutomatonExcept a b m (Automaton m a b)
forall (m :: Type -> Type) e a b.
Monad m =>
m e -> AutomatonExcept a b m e
once_ m (Automaton m a b)
mAutomaton
  Automaton m a b -> AutomatonExcept a b m Void
forall (m :: Type -> Type) a b e.
Monad m =>
Automaton m a b -> AutomatonExcept a b m e
safe Automaton m a b
automaton

-- | 'reactimate's an 'AutomatonExcept' until it throws an exception.
reactimateExcept :: (Monad m) => AutomatonExcept () () m e -> m e
reactimateExcept :: forall (m :: Type -> Type) e.
Monad m =>
AutomatonExcept () () m e -> m e
reactimateExcept AutomatonExcept () () m e
ae = (Either e Void -> e) -> m (Either e Void) -> m e
forall a b. (a -> b) -> m a -> m b
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
fmap ((e -> e) -> (Void -> e) -> Either e Void -> e
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either e -> e
forall a. a -> a
id Void -> e
forall a. Void -> a
absurd) (m (Either e Void) -> m e) -> m (Either e Void) -> m e
forall a b. (a -> b) -> a -> b
$ ExceptT e m Void -> m (Either e Void)
forall e (m :: Type -> Type) a. ExceptT e m a -> m (Either e a)
runExceptT (ExceptT e m Void -> m (Either e Void))
-> ExceptT e m Void -> m (Either e Void)
forall a b. (a -> b) -> a -> b
$ Automaton (ExceptT e m) () () -> ExceptT e m Void
forall (m :: Type -> Type) void.
Monad m =>
Automaton m () () -> m void
reactimate (Automaton (ExceptT e m) () () -> ExceptT e m Void)
-> Automaton (ExceptT e m) () () -> ExceptT e m Void
forall a b. (a -> b) -> a -> b
$ AutomatonExcept () () m e -> Automaton (ExceptT e m) () ()
forall (m :: Type -> Type) a b e.
Monad m =>
AutomatonExcept a b m e -> Automaton (ExceptT e m) a b
runAutomatonExcept AutomatonExcept () () m e
ae

-- | 'reactimate's an 'Automaton' until it returns 'True'.
reactimateB :: (Monad m) => Automaton m () Bool -> m ()
reactimateB :: forall (m :: Type -> Type). Monad m => Automaton m () Bool -> m ()
reactimateB Automaton m () Bool
ae = AutomatonExcept () () m () -> m ()
forall (m :: Type -> Type) e.
Monad m =>
AutomatonExcept () () m e -> m e
reactimateExcept (AutomatonExcept () () m () -> m ())
-> AutomatonExcept () () m () -> m ()
forall a b. (a -> b) -> a -> b
$ Automaton (ExceptT () m) () () -> AutomatonExcept () () m ()
forall (m :: Type -> Type) e a b.
Monad m =>
Automaton (ExceptT e m) a b -> AutomatonExcept a b m e
try (Automaton (ExceptT () m) () () -> AutomatonExcept () () m ())
-> Automaton (ExceptT () m) () () -> AutomatonExcept () () m ()
forall a b. (a -> b) -> a -> b
$ Automaton m () Bool -> Automaton (ExceptT () m) () Bool
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a
       b.
(MonadTrans t, Monad m, Functor (t m)) =>
Automaton m a b -> Automaton (t m) a b
liftS Automaton m () Bool
ae Automaton (ExceptT () m) () Bool
-> Automaton (ExceptT () m) Bool ()
-> Automaton (ExceptT () m) () ()
forall {k} (cat :: k -> k -> Type) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> () -> Automaton (ExceptT () m) Bool ()
forall (m :: Type -> Type) e.
Monad m =>
e -> Automaton (ExceptT e m) Bool ()
throwOn ()

{- | 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@](https://hackage.haskell.org/package/Yampa/docs/FRP-Yampa-Switches.html#v:switch),
with 'Maybe' instead of @Event@.
-}
switch :: (Monad m) => Automaton m a (b, Maybe c) -> (c -> Automaton m a b) -> Automaton m a b
switch :: forall (m :: Type -> Type) a b c.
Monad m =>
Automaton m a (b, Maybe c)
-> (c -> Automaton m a b) -> Automaton m a b
switch Automaton m a (b, Maybe c)
automaton = Automaton (ExceptT c m) a b
-> (c -> Automaton m a b) -> Automaton m a b
forall (m :: Type -> Type) e a b.
Monad m =>
Automaton (ExceptT e m) a b
-> (e -> Automaton m a b) -> Automaton m a b
catchS (Automaton (ExceptT c m) a b
 -> (c -> Automaton m a b) -> Automaton m a b)
-> Automaton (ExceptT c m) a b
-> (c -> Automaton m a b)
-> Automaton m a b
forall a b. (a -> b) -> a -> b
$ proc a
a -> do
  (b
b, Maybe c
me) <- Automaton m a (b, Maybe c)
-> Automaton (ExceptT c m) a (b, Maybe c)
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a
       b.
(MonadTrans t, Monad m, Functor (t m)) =>
Automaton m a b -> Automaton (t m) a b
liftS Automaton m a (b, Maybe c)
automaton -< a
a
  Automaton (ExceptT c m) (Maybe c) (Maybe Any)
forall (m :: Type -> Type) e void.
Monad m =>
Automaton (ExceptT e m) (Maybe e) (Maybe void)
throwMaybe -< Maybe c
me
  Automaton (ExceptT c m) b b
forall (a :: Type -> Type -> Type) b. Arrow a => a b b
returnA -< b
b

{- | 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@](https://hackage.haskell.org/package/Yampa/docs/FRP-Yampa-Switches.html#v:dSwitch),
with 'Maybe' instead of @Event@.
-}
dSwitch :: (Monad m) => Automaton m a (b, Maybe c) -> (c -> Automaton m a b) -> Automaton m a b
dSwitch :: forall (m :: Type -> Type) a b c.
Monad m =>
Automaton m a (b, Maybe c)
-> (c -> Automaton m a b) -> Automaton m a b
dSwitch Automaton m a (b, Maybe c)
sf = Automaton (ExceptT c m) a b
-> (c -> Automaton m a b) -> Automaton m a b
forall (m :: Type -> Type) e a b.
Monad m =>
Automaton (ExceptT e m) a b
-> (e -> Automaton m a b) -> Automaton m a b
catchS (Automaton (ExceptT c m) a b
 -> (c -> Automaton m a b) -> Automaton m a b)
-> Automaton (ExceptT c m) a b
-> (c -> Automaton m a b)
-> Automaton m a b
forall a b. (a -> b) -> a -> b
$ Maybe c
-> Automaton (ExceptT c m) (a, Maybe c) (b, Maybe c)
-> Automaton (ExceptT c m) a b
forall (m :: Type -> Type) c a b.
Functor m =>
c -> Automaton m (a, c) (b, c) -> Automaton m a b
feedback Maybe c
forall a. Maybe a
Nothing (Automaton (ExceptT c m) (a, Maybe c) (b, Maybe c)
 -> Automaton (ExceptT c m) a b)
-> Automaton (ExceptT c m) (a, Maybe c) (b, Maybe c)
-> Automaton (ExceptT c m) a b
forall a b. (a -> b) -> a -> b
$ proc (a
a, Maybe c
me) -> do
  Automaton (ExceptT c m) (Maybe c) (Maybe Any)
forall (m :: Type -> Type) e void.
Monad m =>
Automaton (ExceptT e m) (Maybe e) (Maybe void)
throwMaybe -< Maybe c
me
  Automaton m a (b, Maybe c)
-> Automaton (ExceptT c m) a (b, Maybe c)
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a
       b.
(MonadTrans t, Monad m, Functor (t m)) =>
Automaton m a b -> Automaton (t m) a b
liftS Automaton m a (b, Maybe c)
sf -< a
a