-- | 'MSF's with a 'Writer' monadic layer.
-- This module contains functions to work with 'MSF's that include a 'Writer'
-- monadic layer. This includes functions to create new 'MSF's that include an
-- additional layer, and functions to flatten that layer out of the 'MSF`'s
-- transformer stack.
-- 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 Control.Monad.Trans.MSF.Writer
  ( module Control.Monad.Trans.Writer.Strict
  -- * 'Writer' 'MSF' running and wrapping
  , writerS
  , runWriterS
  ) where

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

-- Internal
import Data.MonadicStreamFunction

-- * 'Writer' 'MSF' running and wrapping

-- | Build an 'MSF' in the 'Writer' monad from one that produces the log as an
-- extra output. This is the opposite of 'runWriterS'.
writerS :: (Functor m, Monad m, Monoid w)
        => MSF m a (w, b) -> MSF (WriterT w m) a b
writerS = morphGS $ \f a -> WriterT $ (\((w, b), c) -> ((b, c), w)) <$> f a

-- | Build an 'MSF' that produces the log as an extra output from one on the
-- 'Writer' monad. This is the opposite of 'writerS'.
runWriterS :: (Functor m, Monad m)
           => MSF (WriterT s m) a b -> MSF m a (s, b)
runWriterS = morphGS $ \f a -> (\((b, c), s) -> ((s, b), c))
         <$> runWriterT (f a)