{- | An 'Automaton' with a 'WriterT' layer outputs an extra monoid value on every step.

It is based on the /strict/ writer monad 'Control.Monad.Trans.Writer.Strict',
so when combining it with other modules such as @mtl@'s,
the strict version has to be included, i.e. 'Control.Monad.Writer.Strict'
instead of 'Control.Monad.Writer' or 'Control.Monad.Writer.Lazy'.
-}
module Data.Automaton.Trans.Writer (
  module Control.Monad.Trans.Writer.Strict,
  writerS,
  runWriterS,
)
where

-- transformers
import Control.Monad.Trans.Writer.Strict hiding (liftCallCC, liftCatch, pass)

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

{- | Convert an extra log output into a 'WriterT' effect.

This is the opposite of 'runWriterS'.
-}
writerS ::
  (Functor m, Monad m, Monoid w) =>
  Automaton m a (w, b) ->
  Automaton (WriterT w m) a b
writerS :: forall (m :: Type -> Type) w a b.
(Functor m, Monad m, Monoid w) =>
Automaton m a (w, b) -> Automaton (WriterT w m) a b
writerS = (forall s.
 (a -> m (Result s (w, b))) -> a -> WriterT w m (Result s b))
-> Automaton m a (w, b) -> Automaton (WriterT w m) a b
forall (m1 :: Type -> Type) (m2 :: Type -> Type) a1 b1 a2 b2.
(Functor m1, Functor m2) =>
(forall s. (a1 -> m1 (Result s b1)) -> a2 -> m2 (Result s b2))
-> Automaton m1 a1 b1 -> Automaton m2 a2 b2
withAutomaton ((forall s.
  (a -> m (Result s (w, b))) -> a -> WriterT w m (Result s b))
 -> Automaton m a (w, b) -> Automaton (WriterT w m) a b)
-> (forall s.
    (a -> m (Result s (w, b))) -> a -> WriterT w m (Result s b))
-> Automaton m a (w, b)
-> Automaton (WriterT w m) a b
forall a b. (a -> b) -> a -> b
$ \a -> m (Result s (w, b))
f a
a -> m (Result s b, w) -> WriterT w m (Result s b)
forall w (m :: Type -> Type) a. m (a, w) -> WriterT w m a
WriterT (m (Result s b, w) -> WriterT w m (Result s b))
-> m (Result s b, w) -> WriterT w m (Result s b)
forall a b. (a -> b) -> a -> b
$ (\(Result s
s (w
w, b
b)) -> (s -> b -> Result s b
forall s a. s -> a -> Result s a
Result s
s b
b, w
w)) (Result s (w, b) -> (Result s b, w))
-> m (Result s (w, b)) -> m (Result s b, w)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> m (Result s (w, b))
f a
a

{- | Convert a 'WriterT' effect into an extra log output.

This is the opposite of 'writerS'.
-}
runWriterS ::
  (Functor m, Monad m) =>
  Automaton (WriterT w m) a b ->
  Automaton m a (w, b)
runWriterS :: forall (m :: Type -> Type) w a b.
(Functor m, Monad m) =>
Automaton (WriterT w m) a b -> Automaton m a (w, b)
runWriterS = (forall s.
 (a -> WriterT w m (Result s b)) -> a -> m (Result s (w, b)))
-> Automaton (WriterT w m) a b -> Automaton m a (w, b)
forall (m1 :: Type -> Type) (m2 :: Type -> Type) a1 b1 a2 b2.
(Functor m1, Functor m2) =>
(forall s. (a1 -> m1 (Result s b1)) -> a2 -> m2 (Result s b2))
-> Automaton m1 a1 b1 -> Automaton m2 a2 b2
withAutomaton ((forall s.
  (a -> WriterT w m (Result s b)) -> a -> m (Result s (w, b)))
 -> Automaton (WriterT w m) a b -> Automaton m a (w, b))
-> (forall s.
    (a -> WriterT w m (Result s b)) -> a -> m (Result s (w, b)))
-> Automaton (WriterT w m) a b
-> Automaton m a (w, b)
forall a b. (a -> b) -> a -> b
$ \a -> WriterT w m (Result s b)
f a
a ->
  (\(Result s
s b
b, w
w) -> s -> (w, b) -> Result s (w, b)
forall s a. s -> a -> Result s a
Result s
s (w
w, b
b))
    ((Result s b, w) -> Result s (w, b))
-> m (Result s b, w) -> m (Result s (w, b))
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> WriterT w m (Result s b) -> m (Result s b, w)
forall w (m :: Type -> Type) a. WriterT w m a -> m (a, w)
runWriterT (a -> WriterT w m (Result s b)
f a
a)