-- |Functions for working with 'Topic's built around monad
-- transformers. These make it possible to, for example, repeat a
-- stateful action to produce a 'Topic''s values.
module Ros.Topic.Transformers where
import Control.Arrow
import qualified Control.Monad.State.Lazy as L
import qualified Control.Monad.State.Strict as S
import Control.Monad.Reader
import Ros.Topic

-- |Run a 'Topic' built around a lazy @'L.StateT' s@ monad using a
-- given initial state.
runTopicState :: Monad m => Topic (L.StateT s m) a -> s -> Topic m a
runTopicState t s = Topic $ do ((x,t'), s') <- L.runStateT (runTopic t) s
                               return (x, runTopicState t' s')

-- |Run a 'Topic' build around a strict @'S.StateT' s@ monad using a
-- given initial state.
runTopicState' :: Monad m => Topic (S.StateT s m) a -> s -> Topic m a
runTopicState' t s = Topic $ do ((x,t'), s') <- S.runStateT (runTopic t) s
                                return (x, runTopicState' t' s')

-- |Run a 'Topic' built around a 'ReaderT r' monad using a value for
-- reading.
runTopicReader :: (Functor m, Monad m) => Topic (ReaderT r m) a -> r -> Topic m a
runTopicReader t r = go t
  where go (Topic ma) = Topic $ second go `fmap` runReaderT ma r

-- |Map a monadic function over a 'Topic', in the process lifting the
-- 'Topic' into a new monad.
liftMap :: (MonadTrans t, Monad m, Monad (t m)) => 
           (a -> t m b) -> Topic m a -> Topic (t m) b
liftMap f = go
  where go (Topic ma) = Topic $ do (x,t') <- lift ma
                                   x' <- f x
                                   return (x', go t')