packages feed

automaton-1.3: src/Data/Automaton/Trans/State.hs

{- | Handle a global 'StateT' layer in an 'Automaton'.

A global state can be hidden by an automaton by making it an internal state.

This module is based on the /strict/ state monad 'Control.Monad.Trans.State.Strict',
so when combining it with other modules such as @mtl@'s,
the strict version has to be included, i.e. 'Control.Monad.State.Strict'
instead of 'Control.Monad.State' or 'Control.Monad.State.Lazy'.
-}
module Data.Automaton.Trans.State (
  module Control.Monad.Trans.State.Strict,
  stateS,
  runStateS,
  runStateS_,
  runStateS__,
)
where

-- base
import Control.Arrow (arr, (>>>))
import Data.Tuple (swap)

-- transformers
import Control.Monad.Trans.State.Strict

-- automaton
import Data.Automaton (Automaton, feedback, withAutomaton)
import Data.Stream.Result (Result (..))

-- * 'State' 'Automaton' running and wrapping

{- | Convert from explicit states to the 'StateT' monad transformer.

The original automaton is interpreted to take a state as input and return the updated state as output.

This is the opposite of 'runStateS'.
-}
stateS :: (Functor m, Monad m) => Automaton m (s, a) (s, b) -> Automaton (StateT s m) a b
stateS = withAutomaton $ \f a -> StateT $ \s ->
  (\(Result s' (s, b)) -> (Result s' b, s))
    <$> f (s, a)

{- | Make the state transition in 'StateT' explicit as 'Automaton' inputs and outputs.

This is the opposite of 'stateS'.
-}
runStateS :: (Functor m, Monad m) => Automaton (StateT s m) a b -> Automaton m (s, a) (s, b)
runStateS = withAutomaton $ \f (s, a) ->
  (\(Result s' b, s) -> Result s' (s, b))
    <$> runStateT (f a) s

{- | Convert global state to internal state of an 'Automaton'.

The current state is output on every step.
-}
runStateS_ ::
  (Functor m, Monad m) =>
  -- | An automaton with a global state effect
  Automaton (StateT s m) a b ->
  -- | The initial global state
  s ->
  Automaton m a (s, b)
runStateS_ automaton s =
  feedback s $
    arr swap >>> runStateS automaton >>> arr (\(s', b) -> ((s', b), s'))

-- | Like 'runStateS_', but don't output the current state.
runStateS__ :: (Functor m, Monad m) => Automaton (StateT s m) a b -> s -> Automaton m a b
runStateS__ automaton s = runStateS_ automaton s >>> arr snd