module Data.Monoid.State where
import qualified Data.Monoid.Transformer as MonoidTrans
import Data.Monoid (Monoid, mempty, mappend, )
import Data.Semigroup (Semigroup, (<>), )
import Data.Functor (Functor, fmap, )
import Data.Function (const, ($), (.), )
import Data.Tuple (fst, snd, )
import Prelude ()
{- |
This resembles the pure State monad.
However, State in transformers is a StateT
and mtl is not Haskell 98.
I hope I have the more natural parameter order for 'evaluate'
in contrast to @mtl@ and @transformers@.
However, it is different from the parameter order of 'run'.
Could also be written as @Monoid.Applicative (Monad.Trans.State s) a@.
-}
newtype T s a = Cons {run :: s -> (a,s)}
pure :: a -> T s a
pure a = Cons $ \s -> (a,s)
evaluate :: s -> T s a -> a
evaluate s m = fst $ run m s
execute :: s -> T s a -> s
execute s m = snd $ run m s
put :: Monoid a => s -> T s a
put s = Cons $ const (mempty, s)
modify :: Monoid a => (s -> s) -> T s a
modify f = Cons $ \s -> (mempty, f s)
instance Semigroup a => Semigroup (T s a) where
Cons x <> Cons y =
Cons $ \s0 ->
let (xr,s1) = x s0
(yr,s2) = y s1
in (xr<>yr, s2)
instance Monoid a => Monoid (T s a) where
mempty = MonoidTrans.lift mempty
mappend (Cons x) (Cons y) =
Cons $ \s0 ->
let (xr,s1) = x s0
(yr,s2) = y s1
in (mappend xr yr, s2)
instance MonoidTrans.C (T s) where
lift x = Cons $ (,) x
instance Functor (T s) where
fmap f (Cons g) = Cons (mapFst f . g)
-- from utility-ht
{-# INLINE mapFst #-}
mapFst :: (a -> c) -> (a,b) -> (c,b)
mapFst f ~(a,b) = (f a, b)