packages feed

mini-2.0.0.0: src/Mini/Transformers/State.hs

-- | Extend a monad with a modifiable environment
module Mini.Transformers.State (
  -- * Type
  StateT (
    StateT
  ),
  runStateT,

  -- * Operations
  get,
  modify,
  put,
) where

import Control.Applicative (
  Alternative,
  empty,
  (<|>),
 )
import Control.Monad (
  ap,
  liftM,
 )
import Control.Monad.IO.Class (
  MonadIO,
  liftIO,
 )
import Data.Bifunctor (
  first,
 )
import Mini.Random.Class (
  Random,
  random,
 )
import Mini.Transformers.Class (
  MonadTrans,
  lift,
 )
import Prelude (
  Applicative,
  Functor,
  Monad,
  MonadFail,
  const,
  fail,
  fmap,
  pure,
  ($),
  (.),
  (<$>),
  (<*>),
  (>>=),
 )

-- Type

-- | A transformer with state /s/, inner monad /m/, return /a/
newtype StateT s m a = StateT
  { runStateT :: s -> m (a, s)
  -- ^ Unwrap a transformer computation with an initial state
  }

instance (Monad m) => Functor (StateT s m) where
  fmap = liftM

instance (Monad m) => Applicative (StateT s m) where
  pure a = StateT $ \s -> pure (a, s)
  (<*>) = ap

instance (Monad m, Alternative m) => Alternative (StateT s m) where
  empty = StateT $ const empty
  m <|> n = StateT $ \s -> runStateT m s <|> runStateT n s

instance (Monad m) => Monad (StateT s m) where
  m >>= k = StateT $ \s -> runStateT m s >>= (\(a, s') -> runStateT (k a) s')

instance MonadTrans (StateT s) where
  lift m = StateT $ \s -> (\a -> (a, s)) <$> m

instance (MonadFail m) => MonadFail (StateT s m) where
  fail = StateT . const . fail

instance (MonadIO m) => MonadIO (StateT s m) where
  liftIO = lift . liftIO

instance (Monad m, Random a) => Random (StateT s m a) where
  random = first pure . random

-- Operations

-- | Fetch the current state
get :: (Monad m) => StateT s m s
get = StateT $ \s -> pure (s, s)

-- | Update the current state with an operation
modify :: (Monad m) => (s -> s) -> StateT s m ()
modify f = StateT $ \s -> pure ((), f s)

-- | Overwrite the current state with a value
put :: (Monad m) => s -> StateT s m ()
put s = StateT . const $ pure ((), s)