packages feed

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

-- | Extend a monad with a read-only environment
module Mini.Transformers.Reader (
  -- * Type
  ReaderT (
    ReaderT
  ),
  runReaderT,

  -- * Operations
  ask,
  local,
) 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 read-only /r/, inner monad /m/, return /a/
newtype ReaderT r m a = ReaderT
  { runReaderT :: r -> m a
  -- ^ Unwrap a transformer computation with an initial read-only value
  }

instance (Monad m) => Functor (ReaderT r m) where
  fmap = liftM

instance (Monad m) => Applicative (ReaderT r m) where
  pure = lift . pure
  (<*>) = ap

instance (Monad m, Alternative m) => Alternative (ReaderT r m) where
  empty = lift empty
  m <|> n = ReaderT $ \r -> runReaderT m r <|> runReaderT n r

instance (Monad m) => Monad (ReaderT r m) where
  m >>= k = ReaderT $ \r -> runReaderT m r >>= (`runReaderT` r) . k

instance MonadTrans (ReaderT r) where
  lift = ReaderT . const

instance (MonadFail m) => MonadFail (ReaderT r m) where
  fail = lift . fail

instance (MonadIO m) => MonadIO (ReaderT r m) where
  liftIO = lift . liftIO

instance (Monad m, Random a) => Random (ReaderT r m a) where
  random = first pure . random

-- Operations

-- | Fetch the read-only environment
ask :: (Monad m) => ReaderT r m r
ask = ReaderT pure

-- | Run a computation in a modified environment
local :: (r -> r') -> ReaderT r' m a -> ReaderT r m a
local f m = ReaderT $ runReaderT m . f