{- |
@MonadPlus@ transformers. There are also @Monad@ and @MonadFix@ transformes, see the corresponding modules.

Note that each @MonadPlus@ transformer is also a @Monad@ transformer.
-}
module Control.Monad.Trans.MonadPlus (MonadP, TransP(..), instP, mzero', mplus') where
import Control.Monad.List
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Trans.Category
import Control.Monad.Trans.Monad
import Control.Monad.Writer
-- | @MonadP m@ is actually a free @MonadPlus@ generated by @m@. @MonadP@ is a monad itself (on the @(* -> *)@ category), as usually happens with free structures.
newtype MonadP m x = MonadP {bindP :: forall n. MonadPlus n => (m :-> n) -> n x}
-- | A @MonadPlus@ is nothing but an algebra over the @MonadP@ monad. @instP@ provides it's structure map.
instP :: MonadPlus m => Inst MonadP m
instP mmx = bindP mmx id
-- | Sometimes we need an @instance MonadPlus T@, while everything we've got is @InstP MonadP T@. In this case, @mzero'@ serves as a @mzero@ substitution.
mzero' :: Inst MonadP m -> m x
mzero' i = i $ MonadP {bindP = \_ -> mzero}
-- | Sometimes we need an @instance Monad T@, while everything we've got is @Inst MonadP T@. In this case, @mplus'@ serves as a @mplus@ substitution.
mplus' :: Inst MonadP m -> m x -> m x -> m x
mplus' i mx1 mx2 = i $ MonadP {bindP = \mor -> mor mx1 `mplus` mor mx2}
-- | A composable @MonadPlus@ transformer.
class TransM t => TransP t where
  -- | You shoudn't (and probably can't) use *anything* except for @'instP'@, defined in this very module, as @transPInst@.
  --
  -- If you define @instance TransP T where transPInst = instP@, then you would also need to define @instance MonadPlus m => MonadPlus (T m)@ somewhere in your code.
  transPInst :: MonadPlus m => Inst MonadP (t m)
instance (MonadPlus m, TransP t) => MonadPlus (t :$ m) where
    mzero = ApplyF {runApplyF = mzero' transPInst}
    tm1 `mplus` tm2 = ApplyF {runApplyF = runApplyF tm1 `mplus1` runApplyF tm2} where mplus1 = mplus' transPInst
deriving instance (MonadPlus m, TransP t1, TransP t2) => MonadPlus ((t2 :. t1) m)
instance (TransP t1, TransP t2) => TransP (t2 :. t1) where transPInst = instP
instance TransP ListT where transPInst = instP
instance TransP (ReaderT r) where transPInst = instP
instance TransP (StateT s) where transPInst = instP
instance Monoid w => TransP (WriterT w) where transPInst = instP