module Control.Monad.Trans.Reader.Extras where

import Control.Monad.Trans.Reader
import Control.Monad.Morph
import Data.Functor.Identity

-- | This function combines two different monadic effects,
-- where one of the effects is a reader effect and the other is a
-- producing effect.
--
-- This version results in the reader monad's inner effect to be wrapped
-- around the producing effect.
-- This requires the Reader inner effect to be an MFunctor on Identity.
--
-- This can enable past-Dependence.
-- Elm has foldp : (a -> state -> state) -> state -> Signal a -> Signal state
-- This is equivalent to a creating a @StateT state (Signal m) ()@
--
-- 'runReaderM' is a more general form of @StateT state (Signal m) ()@ where
-- given a reader monad to transform "a" to "c" with effects, and an "as"
-- monad that produces "a"s with other effects, run the result of "as" through
-- the reader monad to produce "c"s with both effects.
-- @
-- runReaderM :: Monad m => Reader a (State s) c -> m a                      -> StateT s m c
-- runReaderM ::            Reader a (State s) c -> Signal STM a             -> StateT state (Signal STM) c
-- runReaderM ::            Reader a (State s) c -> Pipes.Concurrent.Input a -> StateT state Pipes.Concurrent.Input c
-- @
runReaderM :: (Monad m, Monad (t m), MonadTrans t, MFunctor t)
  => ReaderT a (t Identity) c -> m a -> t m c
runReaderM c as = do
  a <- lift as
  hoist generalize $ runReaderT c a

-- | An alternate form of runReaderM where the producing effect is
-- wrapped around the reader monad's inner effect.
-- This requires the producing effect to be an MFunctor on Identity.
runReaderM' :: (Monad m, Monad (t m), MonadTrans t, MFunctor t)
  => ReaderT a m c -> (t Identity) a -> t m c
runReaderM' c as = do
  a <- hoist generalize as
  lift $ runReaderT c a