-- | This module combines the wrapping and running functions
--   for the 'Reader', 'Writer' and 'State' monad layers in a single layer.
--
-- It is based on the _strict_ 'RWS' monad 'Control.Monad.Trans.RWS.Strict',
-- so when combining it with other modules such as @mtl@'s,
-- the strict version has to be included, i.e. 'Control.Monad.RWS.Strict'
-- instead of 'Control.Monad.RWS' or 'Control.Monad.RWS.Lazy'.
module Control.Monad.Trans.MSF.RWS
  ( module Control.Monad.Trans.MSF.RWS
  , module Control.Monad.Trans.RWS.Strict
  ) where

-- External
import Control.Monad.Trans.RWS.Strict
  hiding (liftCallCC, liftCatch) -- Avoid conflicting exports
import Data.Monoid
import Data.Functor ((<$>))

-- Internal
import Control.Monad.Trans.MSF.GenLift
import Data.MonadicStreamFunction

-- * 'RWS' (Reader-Writer-State) monad

-- | Run the 'RWST' layer by making the state variables explicit.
runRWSS :: (Monad m, Monoid w)
        => MSF (RWST r w s m) a b
        -> MSF m (r, s, a) (w, s, b)
runRWSS = transS transformInput transformOutput
  where
    transformInput  (_, _, a) = return a
    transformOutput (r, s, _) msfaction = sym <$> runRWST msfaction r s
    sym ((b, msf'), s, w) = ((w, s, b), msf')

-- | Wrap an 'MSF' with explicit state variables in 'RWST' monad.
rwsS :: (Monad m, Monoid w)
     => MSF m (r, s, a) (w, s, b)
     -> MSF (RWST r w s m) a b
rwsS = lifterS wrapRWST
  where
    wrapRWST :: Monad m
             => ((r, s, a) -> m ((w, s, b), c)) -> a -> RWST r w s m (b, c)
    wrapRWST f a = RWST $ \r s -> do
      ((w, s', b), c) <- f (r, s, a)
      return ((b, c), s', w)