-- |
-- Module      : Basement.Compat.MonadTrans
-- License     : BSD-style
-- Maintainer  : Psychohistorians
-- Stability   : experimental
-- Portability : portable
--
-- An internal and really simple monad transformers,
-- without any bells and whistse.
module Basement.Compat.MonadTrans
    ( State(..)
    , Reader(..)
    ) where

import Basement.Compat.Base
import Control.Monad ((>=>))

-- | Simple State monad
newtype State s m a = State { runState :: s -> m (a, s) }

instance Monad m => Functor (State s m) where
    fmap f fa = State $ runState fa >=> (\(a, s2) -> return (f a, s2))
instance Monad m => Applicative (State s m) where
    pure a = State $ \st -> return (a,st)
    fab <*> fa = State $ \s1 -> do
        (ab,s2) <- runState fab s1
        (a,s3)  <- runState fa s2
        return (ab a, s3)
instance Monad m => Monad (State r m) where
    return a = State $ \st -> return (a,st)
    ma >>= mb = State $ \s1 -> do
        (a,s2) <- runState ma s1
        runState (mb a) s2

-- | Simple Reader monad
newtype Reader r m a = Reader { runReader :: r -> m a }

instance Monad m => Functor (Reader r m) where
    fmap f fa = Reader $ runReader fa >=> (\a -> return (f a))
instance Monad m => Applicative (Reader r m) where
    pure a = Reader $ \_ -> return a
    fab <*> fa = Reader $ \r -> do
        a  <- runReader fa r
        ab <- runReader fab r
        return $ ab a
instance Monad m => Monad (Reader r m) where
    return a = Reader $ \_ -> return a
    ma >>= mb = Reader $ \r -> do
        a <- runReader ma r
        runReader (mb a) r