automaton-1.6: Effectful streams and automata in coalgebraic encoding
Safe HaskellNone
LanguageHaskell2010

Data.Automaton

Synopsis

Constructing automata

newtype Automaton (m :: Type -> Type) a b Source #

An effectful automaton in coalgebraic encoding.

  • m: The monad in which the automaton performs side effects.
  • a: The type of inputs the automaton constantly consumes.
  • b: The type of outputs the automaton constantly produces.

An effectful automaton with input a is the same as an effectful stream with the additional effect of reading an input value a on every step. This is why automata are defined here as streams.

The API of automata follows that of streams (StreamT and OptimizedStreamT) closely. The prominent addition in automata is now that they are instances of the Category, Arrow, Profunctor, and related type classes. This allows for more ways of creating or composing them.

For example, you can sequentially and parallely compose two automata: @ automaton1 :: Automaton m a b automaton2 :: Automaton m b c

sequentially :: Automaton m a c sequentially = automaton1 >>> automaton2

inParallel :: Automaton m (a, b) (b, c) inParallel = automaton1 *** automaton2 @ In sequential composition, the output of the first automaton is passed as input to the second one. In parallel composition, both automata receive input simulataneously and process it independently.

Through the Arrow type class, you can use arr to create an automaton from a pure function, and more generally use the arrow syntax extension to define automata.

Constructors

Automaton 

Instances

Instances details
Monad m => Category (Automaton m :: Type -> Type -> Type) Source # 
Instance details

Defined in Data.Automaton

Methods

id :: Automaton m a a #

(.) :: Automaton m b c -> Automaton m a b -> Automaton m a c #

Monad m => Arrow (Automaton m) Source # 
Instance details

Defined in Data.Automaton

Methods

arr :: (b -> c) -> Automaton m b c #

first :: Automaton m b c -> Automaton m (b, d) (c, d) #

second :: Automaton m b c -> Automaton m (d, b) (d, c) #

(***) :: Automaton m b c -> Automaton m b' c' -> Automaton m (b, b') (c, c') #

(&&&) :: Automaton m b c -> Automaton m b c' -> Automaton m b (c, c') #

Monad m => ArrowChoice (Automaton m) Source # 
Instance details

Defined in Data.Automaton

Methods

left :: Automaton m b c -> Automaton m (Either b d) (Either c d) #

right :: Automaton m b c -> Automaton m (Either d b) (Either d c) #

(+++) :: Automaton m b c -> Automaton m b' c' -> Automaton m (Either b b') (Either c c') #

(|||) :: Automaton m b d -> Automaton m c d -> Automaton m (Either b c) d #

MonadFix m => ArrowLoop (Automaton m) Source #

Caution, this can make your program hang. Try to use feedback or unfold where possible, or combine loop with delay.

Instance details

Defined in Data.Automaton

Methods

loop :: Automaton m (b, d) (c, d) -> Automaton m b c #

(Monad m, Alternative m) => ArrowPlus (Automaton m) Source # 
Instance details

Defined in Data.Automaton

Methods

(<+>) :: Automaton m b c -> Automaton m b c -> Automaton m b c #

(Monad m, Alternative m) => ArrowZero (Automaton m) Source # 
Instance details

Defined in Data.Automaton

Methods

zeroArrow :: Automaton m b c #

Monad m => Choice (Automaton m) Source # 
Instance details

Defined in Data.Automaton

Methods

left' :: Automaton m a b -> Automaton m (Either a c) (Either b c) #

right' :: Automaton m a b -> Automaton m (Either c a) (Either c b) #

Monad m => Cochoice (Automaton m) Source # 
Instance details

Defined in Data.Automaton

Methods

unleft :: Automaton m (Either a d) (Either b d) -> Automaton m a b #

unright :: Automaton m (Either d a) (Either d b) -> Automaton m a b #

Monad m => Strong (Automaton m) Source # 
Instance details

Defined in Data.Automaton

Methods

first' :: Automaton m a b -> Automaton m (a, c) (b, c) #

second' :: Automaton m a b -> Automaton m (c, a) (c, b) #

Monad m => Traversing (Automaton m) Source #

Step an automaton several steps at once, depending on how long the input is.

Instance details

Defined in Data.Automaton

Methods

traverse' :: Traversable f => Automaton m a b -> Automaton m (f a) (f b) #

wander :: (forall (f :: Type -> Type). Applicative f => (a -> f b) -> s -> f t) -> Automaton m a b -> Automaton m s t #

Functor m => Profunctor (Automaton m) Source # 
Instance details

Defined in Data.Automaton

Methods

dimap :: (a -> b) -> (c -> d) -> Automaton m b c -> Automaton m a d #

lmap :: (a -> b) -> Automaton m b c -> Automaton m a c #

rmap :: (b -> c) -> Automaton m a b -> Automaton m a c #

(#.) :: forall a b c q. Coercible c b => q b c -> Automaton m a b -> Automaton m a c #

(.#) :: forall a b c q. Coercible b a => Automaton m b c -> q a b -> Automaton m a c #

Alternative m => Alternative (Automaton m a) Source # 
Instance details

Defined in Data.Automaton

Methods

empty :: Automaton m a a0 #

(<|>) :: Automaton m a a0 -> Automaton m a a0 -> Automaton m a a0 #

some :: Automaton m a a0 -> Automaton m a [a0] #

many :: Automaton m a a0 -> Automaton m a [a0] #

Applicative m => Applicative (Automaton m a) Source # 
Instance details

Defined in Data.Automaton

Methods

pure :: a0 -> Automaton m a a0 #

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

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

(*>) :: Automaton m a a0 -> Automaton m a b -> Automaton m a b #

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

Functor m => Functor (Automaton m a) Source # 
Instance details

Defined in Data.Automaton

Methods

fmap :: (a0 -> b) -> Automaton m a a0 -> Automaton m a b #

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

Selective m => Selective (Automaton m a) Source # 
Instance details

Defined in Data.Automaton

Methods

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

Align m => Align (Automaton m a) Source # 
Instance details

Defined in Data.Automaton

Methods

nil :: Automaton m a a0 #

Semialign m => Semialign (Automaton m a) Source #

Run both automata in parallel and use Semialign m to decide which automaton produces output. If you understand m as an effect that models the passage of time, then align runs both automata concurrently.

Instance details

Defined in Data.Automaton

Methods

align :: Automaton m a a0 -> Automaton m a b -> Automaton m a (These a0 b) #

alignWith :: (These a0 b -> c) -> Automaton m a a0 -> Automaton m a b -> Automaton m a c #

(Applicative m, Floating b) => Floating (Automaton m a b) Source # 
Instance details

Defined in Data.Automaton

Methods

pi :: Automaton m a b #

exp :: Automaton m a b -> Automaton m a b #

log :: Automaton m a b -> Automaton m a b #

sqrt :: Automaton m a b -> Automaton m a b #

(**) :: Automaton m a b -> Automaton m a b -> Automaton m a b #

logBase :: Automaton m a b -> Automaton m a b -> Automaton m a b #

sin :: Automaton m a b -> Automaton m a b #

cos :: Automaton m a b -> Automaton m a b #

tan :: Automaton m a b -> Automaton m a b #

asin :: Automaton m a b -> Automaton m a b #

acos :: Automaton m a b -> Automaton m a b #

atan :: Automaton m a b -> Automaton m a b #

sinh :: Automaton m a b -> Automaton m a b #

cosh :: Automaton m a b -> Automaton m a b #

tanh :: Automaton m a b -> Automaton m a b #

asinh :: Automaton m a b -> Automaton m a b #

acosh :: Automaton m a b -> Automaton m a b #

atanh :: Automaton m a b -> Automaton m a b #

log1p :: Automaton m a b -> Automaton m a b #

expm1 :: Automaton m a b -> Automaton m a b #

log1pexp :: Automaton m a b -> Automaton m a b #

log1mexp :: Automaton m a b -> Automaton m a b #

(Applicative m, Num b) => Num (Automaton m a b) Source # 
Instance details

Defined in Data.Automaton

Methods

(+) :: Automaton m a b -> Automaton m a b -> Automaton m a b #

(-) :: Automaton m a b -> Automaton m a b -> Automaton m a b #

(*) :: Automaton m a b -> Automaton m a b -> Automaton m a b #

negate :: Automaton m a b -> Automaton m a b #

abs :: Automaton m a b -> Automaton m a b #

signum :: Automaton m a b -> Automaton m a b #

fromInteger :: Integer -> Automaton m a b #

(Applicative m, Fractional b) => Fractional (Automaton m a b) Source # 
Instance details

Defined in Data.Automaton

Methods

(/) :: Automaton m a b -> Automaton m a b -> Automaton m a b #

recip :: Automaton m a b -> Automaton m a b #

fromRational :: Rational -> Automaton m a b #

(Eq s, Floating s, VectorSpace v s, Applicative m) => VectorSpace (Automaton m a v) (Automaton m a s) Source # 
Instance details

Defined in Data.Automaton

Methods

zeroVector :: Automaton m a v #

(*^) :: Automaton m a s -> Automaton m a v -> Automaton m a v #

(^/) :: Automaton m a v -> Automaton m a s -> Automaton m a v #

(^+^) :: Automaton m a v -> Automaton m a v -> Automaton m a v #

(^-^) :: Automaton m a v -> Automaton m a v -> Automaton m a v #

negateVector :: Automaton m a v -> Automaton m a v #

dot :: Automaton m a v -> Automaton m a v -> Automaton m a s #

norm :: Automaton m a v -> Automaton m a s #

normalize :: Automaton m a v -> Automaton m a v #

unfold Source #

Arguments

:: forall (m :: Type -> Type) s a b. Applicative m 
=> s

The initial state

-> (a -> s -> Result s b)

The step function

-> Automaton m a b 

Create an Automaton from a state and a pure step function.

unfoldM Source #

Arguments

:: s

The initial state

-> (a -> s -> m (Result s b))

The step function

-> Automaton m a b 

Create an Automaton from a state and an effectful step function.

unfold_ Source #

Arguments

:: forall (m :: Type -> Type) s a. Applicative m 
=> s

The initial state

-> (a -> s -> s)

The step function

-> Automaton m a s 

Like unfold, but output the current state.

arrM :: Functor m => (a -> m b) -> Automaton m a b Source #

Consume an input and produce output effectfully, without keeping internal state

constM :: Functor m => m b -> Automaton m a b Source #

Produce output effectfully, without keeping internal state

hoistS :: Monad m => (forall x. m x -> n x) -> Automaton m a b -> Automaton n a b Source #

Apply an arbitrary monad morphism to an automaton.

liftS :: 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 Source #

Lift the monad of an automaton to a transformer.

feedback Source #

Arguments

:: forall (m :: Type -> Type) c a b. Functor m 
=> c

The additional internal state

-> Automaton m (a, c) (b, c)

The original automaton

-> Automaton m a b 

Extend the internal state and feed back part of the output to the next input.

This is one of the fundamental ways to incorporate recursive dataflow in automata. Given an automaton which consumes an additional input and produces an additional output, the state of the automaton is extended by a further value. This value is used as the additional input, and the resulting additional output is stored in the internal state for the next step.

Running automata

stepAutomaton :: Functor m => Automaton m a b -> a -> m (Result (Automaton m a b) b) Source #

Run one step of an automaton.

This consumes an input value, performs a side effect, and returns an updated automaton together with an output value.

reactimate :: Monad m => Automaton m () () -> m void Source #

Run an automaton with trivial input and output indefinitely.

If the input and output of an automaton does not contain information, all of its meaning is in its effects. This function runs the automaton indefinitely. Since it will never return with a value, this function also has no output (its output is void). The only way it can return is if m includes some effect of termination, e.g. Maybe or Either could terminate with a Nothing or Left value, or IO can raise an exception.

embed Source #

Arguments

:: Monad m 
=> Automaton m a b

The automaton to run

-> [a]

The input values

-> m [b] 

Run an automaton with given input, for a given number of steps.

Especially for tests and batch processing, it is useful to step an automaton with given input.

Modifying automata

withAutomaton :: (Functor m1, Functor m2) => (forall s. (a1 -> m1 (Result s b1)) -> a2 -> m2 (Result s b2)) -> Automaton m1 a1 b1 -> Automaton m2 a2 b2 Source #

Change the input and output type and effect of an automaton without changing its state type.

withAutomaton_ :: (Functor m1, Functor m2) => (forall s. m1 (Result s b1) -> m2 (Result s b2)) -> Automaton m1 a b1 -> Automaton m2 a b2 Source #

Change the output type and effect of an automaton without changing its state type.

Traversing automata

mapMaybeS :: forall (m :: Type -> Type) a b. Monad m => Automaton m a b -> Automaton m (Maybe a) (Maybe b) Source #

Only step the automaton if the input is Just.

traverseS :: forall (m :: Type -> Type) f a b. (Monad m, Traversable f) => Automaton m a b -> Automaton m (f a) (f b) Source #

Use an Automaton with a variable amount of input.

traverseS_ :: forall (m :: Type -> Type) f a b. (Monad m, Traversable f) => Automaton m a b -> Automaton m (f a) () Source #

Like traverseS, discarding the output.

parallelyList :: forall (m :: Type -> Type) a b. Applicative m => Automaton m a b -> Automaton m [a] [b] Source #

Launch arbitrarily many copies of the automaton in parallel.

  • The copies of the automaton are launched on demand as the input lists grow.
  • The n-th copy will always receive the n-th input.
  • If the input list has length n, the n+1-th automaton copy will not be stepped.

Caution: Uses memory of the order of the largest list that was ever input during runtime.

parallely :: forall (m :: Type -> Type) t a b. (Applicative m, Witherable t, Align t) => Automaton m a b -> Automaton m (t a) (t b) Source #

Launch many copies of the automaton in parallel, depending on the input shape.

  • This generalises parallelyList from lists to arbitrary Witherables satisfying Align such as Maps, Seq'uences, and other data structures.
  • The copies of the automaton are launched on demand as the input shape changes in such a way that there are new positions.
  • The automaton copy on a particular position will always receive the input from that position.
  • Only those automaton copies on positions with a matching input will be stepped.

For example, if the first input is a map with keys k1 and k2, two copies will be started, one for each key. If the next input map has keys k1 and k3, the first automaton at key k1 will be stepped, the copy at k2 will not be stepped and keeps its state, and a new copy will be launched at k3.

Caution: Uses memory of the order of the largest input that was ever input during runtime.

Interaction with StreamT

fromStream :: forall (m :: Type -> Type) a any. Monad m => StreamT m a -> Automaton m any a Source #

Create an Automaton from a stream.

It will ignore its input.

toStreamT :: forall (m :: Type -> Type) a b. Functor m => Automaton m a b -> StreamT (ReaderT a m) b Source #

Create a StreamT from an Automaton.

The resulting stream can read the current input as an effect in ReaderT.

handleAutomaton_ :: forall (m :: Type -> Type) a b i. Monad m => (forall (m1 :: Type -> Type). Monad m1 => StreamT m1 a -> StreamT m1 b) -> Automaton m i a -> Automaton m i b Source #

Given a transformation of streams, apply it to an automaton, without changing the input.

handleAutomatonF_ :: forall (m :: Type -> Type) a b i. Functor m => (forall (m1 :: Type -> Type). Functor m1 => StreamT m1 a -> StreamT m1 b) -> Automaton m i a -> Automaton m i b Source #

Like handleAutomaton_, but with fewer constraints.

handleAutomaton :: forall (m :: Type -> Type) a b c (n :: Type -> Type) d. Functor m => (StreamT (ReaderT a m) b -> StreamT (ReaderT c n) d) -> Automaton m a b -> Automaton n c d Source #

Given a transformation of streams, apply it to an automaton. The input can be accessed through the ReaderT effect.

Buffering

concatS :: forall (m :: Type -> Type) a b. Monad m => Automaton m a [b] -> Automaton m a b Source #

Buffer the output of an automaton. See concatS.

The input for the automaton is not buffered. For example, if concatS automaton receives one input a and automaton produces 10 bs from it, then the next 9 inputs will be ignored.

Handling effects

handleEffect Source #

Arguments

:: (Monad m, Monad eff, Functor sig) 
=> (forall x. sig x -> eff x)

Send a declarative effect in the signature to the effect carrier monad.

-> (forall x. eff x -> m (sig x))

Interpret the effect in m, returning its result in the signature.

-> Automaton eff a b 
-> Automaton m a (sig b) 

Continuously interpret a first order effect.

Several types are relevant here:

  • sig: An effect signature functor, that encodes one effect. For example, Either e for raising exceptions of type e, or (w, ) for a logging effect.
  • eff: A monad that carries the effect. This can be a monad transformer stack including a transformer corresponding to sig, such as ExceptT for Either. It can also be the Eff monad of an effect library such as polysemy, bluefin, effectful and so on.
  • m: The underlying monad in which the interpretation is performed, think "eff without the effects from sig".

This function takes two functions, one to create effects in eff from the signature, and the other to fully interpret them in m, storing the complete effect information in sig again. It then executes the given automaton, extracting the effect by interpretation, and sending it back in. The execution semantics is that of the monad eff, while the pure effect of the whole computation is returned in the output, encoded in sig.

For examples, see handleEffect.

Examples

withSideEffect Source #

Arguments

:: Monad m 
=> (a -> m b)

For every value passing through the automaton, this function is called and the resulting side effect performed.

-> Automaton m a a 

Pass through a value unchanged, and perform a side effect depending on it

accumulateWith Source #

Arguments

:: forall (m :: Type -> Type) a b. Monad m 
=> (a -> b -> b)

The accumulation function

-> b

The initial accumulator

-> Automaton m a b 

Accumulate the input, output the accumulator.

mappendFrom :: forall w (m :: Type -> Type). (Monoid w, Monad m) => w -> Automaton m w w Source #

Like accumulateWith, with mappend as the accumulation function.

The new values are mappended from the left.

mappendFromR :: forall w (m :: Type -> Type). (Monoid w, Monad m) => w -> Automaton m w w Source #

Like mappendFrom, but mappending new values from the right.

delay Source #

Arguments

:: forall (m :: Type -> Type) a. Applicative m 
=> a

The value to output on the first step

-> Automaton m a a 

Delay the input by one step.

prepend :: forall (m :: Type -> Type) b a. Monad m => b -> Automaton m a b -> Automaton m a b Source #

Delay an automaton by one step by prepending one value to the output.

On the first step, the given initial output is returned. On all subsequent steps, the automaton is stepped with the previous input.

mappendS :: forall w (m :: Type -> Type). (Monoid w, Monad m) => Automaton m w w Source #

Like mappendFrom, initialised at mempty.

sumFrom :: forall v s (m :: Type -> Type). (VectorSpace v s, Monad m) => v -> Automaton m v v Source #

Sum up all inputs so far, with an explicit initial value.

sumS :: forall (m :: Type -> Type) v s. (Monad m, VectorSpace v s) => Automaton m v v Source #

Like sumFrom, initialised at 0.

sumN :: forall (m :: Type -> Type) a. (Monad m, Num a) => Automaton m a a Source #

Sum up all inputs so far, initialised at 0.

count :: forall n (m :: Type -> Type) a. (Num n, Monad m) => Automaton m a n Source #

Count the natural numbers, beginning at 1.

lastS :: forall (m :: Type -> Type) a. Monad m => a -> Automaton m (Maybe a) a Source #

Remembers the last Just value, defaulting to the given initialisation value.

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

Call the monadic action once on the first tick and provide its result indefinitely.

initialised_ :: Monad m => m b -> Automaton m a b Source #

Like initialised, but ignores the input.