packages feed

rebound-0.1.0.0: src/Rebound/MonadScoped.hs

-- |
-- Description: Scoped variants of some monads
--
-- Provides scoped variants of monads from [mtl](https://hackage.haskell.org/package/mtl).

module Rebound.MonadScoped
  ( MonadScopedReader (..),
    ScopedReader (..),
    ScopedReaderT (..),
    asksS,
    runScopedReader,
    MonadScopedState (..),
    ScopedState (..),
    ScopedStateT (..),
    evalScopedState,
    evalScopedStateT,
    execScopedState,
    execScopedStateT,
    modifyS,
    getsS
  )
where

import Control.Monad (liftM2, (>=>))
import Control.Monad.Error.Class (MonadError (..))
import Control.Monad.Identity (Identity (runIdentity))
import Control.Monad.Reader (MonadReader (ask, local), asks)
import Control.Monad.Writer (MonadWriter (..))
import Data.Kind (Type)
import Data.Nat (Nat (S))
import Data.SNat (type (+))

-----------------------------------------------------------------------
-- Reader class
-----------------------------------------------------------------------

-- | Scoped variant of 'Control.Monad.Reader.MonadReader'.
--
-- __Note__: the "environment" mentioned here as nothing to do with 'Rebound.Env.Env'!
class (forall n. Monad (m n)) => MonadScopedReader e m | m -> e where
  {-# MINIMAL (askS | readerS), localS #-}
  -- | Retrieve the environment.
  askS :: m n (e n)
  askS = readerS id
  -- | Run a function in an altered environment.
  localS :: (e n -> e n') -> m n' a -> m n a
  -- | Retrieve a function of the environment.
  readerS :: (e n -> a) -> m n a
  readerS f = f <$> askS

-- | Retrieve the environment.
asksS :: (MonadScopedReader e m) => (e n -> a) -> m n a
asksS = readerS

-----------------------------------------------------------------------
-- Reader monad
-----------------------------------------------------------------------

-- | Computations that need a (read-only) environment.
type ScopedReader e n a = ScopedReaderT e Identity n a

-- | Run the computation with the provided environment.
runScopedReader :: ScopedReader e n a -> e n -> a
runScopedReader c m = runIdentity $ runScopedReaderT c m

-----------------------------------------------------------------------
-- Reader transformer
-----------------------------------------------------------------------

-- | A scoped variant of 'Control.Monad.Reader.ReaderT'.
newtype ScopedReaderT e m n a = ScopedReaderT {runScopedReaderT :: e n -> m a}
  deriving (Functor)

instance (Applicative m) => Applicative (ScopedReaderT e m n) where
  pure f = ScopedReaderT $ \x -> pure f
  ScopedReaderT f <*> ScopedReaderT x = ScopedReaderT (\e -> f e <*> x e)

instance (Monad m) => Monad (ScopedReaderT e m n) where
  ScopedReaderT m >>= k = ScopedReaderT $ \e ->
    m e >>= (\v -> let x = k v in runScopedReaderT x e)

instance (MonadReader r m) => MonadReader r (ScopedReaderT e m n) where
  ask = ScopedReaderT $ const ask
  local f m = ScopedReaderT (local f . runScopedReaderT m)

instance (MonadError e m) => MonadError e (ScopedReaderT se m n) where
  throwError e = ScopedReaderT $ const (throwError e)
  catchError m k = ScopedReaderT $ \s -> runScopedReaderT m s `catchError` (\err -> runScopedReaderT (k err) s)

instance (MonadWriter w m) => MonadWriter w (ScopedReaderT e m n) where
  writer w = ScopedReaderT $ const (writer w)
  listen m = ScopedReaderT $ \s -> listen $ runScopedReaderT m s
  pass m = ScopedReaderT $ \s -> pass $ runScopedReaderT m s

instance (Monad m) => MonadScopedReader e (ScopedReaderT e m) where
  askS = ScopedReaderT return
  localS f (ScopedReaderT g) = ScopedReaderT $ g . f

-----------------------------------------------------------------------
-- State class
-----------------------------------------------------------------------

-- | Scoped variant of 'Control.Monad.State.MonadState'.
class (forall n. Monad (m n)) => MonadScopedState s m | m -> s where
  {-# MINIMAL rescope, (stateS | (getS, putS)) #-}
  -- | Change the scope of the environment, run a function, and change back the scope.
  rescope :: (s n -> s n') -> (s n' -> s n) -> m n' a -> m n a

  -- | Retrieve the state.
  getS :: m n (s n)
  getS = stateS $ \s -> (s, s)

  -- | Set the state.
  putS :: s n -> m n ()
  putS s = stateS $ const ((), s)

  -- | Lift a function into a monadic computation.
  stateS :: (s n -> (a, s n)) -> m n a
  stateS f = do
    s <- getS
    let (v, s') = f s
    putS s'
    return v

-- | Apply a function to the state.
modifyS :: (MonadScopedState s m) => (s n -> s n) -> m n ()
modifyS f = do
  s <- getS
  putS $ f s

-- | Retrieve a function of the state.
getsS :: (MonadScopedState s m) => (s n -> a) -> m n a
getsS f = f <$> getS

-----------------------------------------------------------------------
-- State monad
-----------------------------------------------------------------------

-- | Computations that need a state.
type ScopedState s n a = ScopedStateT s Identity n a

-- | Run the computation with the provided state, and return the result as well as the final state.
runScopedState :: ScopedState s n a -> s n -> (a, s n)
runScopedState m s = runIdentity $ runScopedStateT m s

-- | Run the computation with the provided state, and return the result.
evalScopedState :: ScopedState s n a -> s n -> a
evalScopedState m s = runIdentity $ evalScopedStateT m s

-- | Run the computation with the provided state, and return the final state.
execScopedState :: ScopedState s n a -> s n -> s n
execScopedState m s = runIdentity $ execScopedStateT m s

-----------------------------------------------------------------------
-- State transformer
-----------------------------------------------------------------------

-- | A scoped variant of 'Control.Monad.State.StateT'.
newtype ScopedStateT s m n a = ScopedStateT {runScopedStateT :: s n -> m (a, s n)}
  deriving (Functor)

-- | Run the computation with the provided state, and return the result.
evalScopedStateT :: (Functor m) => ScopedStateT s m n a -> s n -> m a
evalScopedStateT m s = fst <$> runScopedStateT m s

-- | Run the computation with the provided state, and return the final state.
execScopedStateT :: (Functor m) => ScopedStateT s m n a -> s n -> m (s n)
execScopedStateT m s = snd <$> runScopedStateT m s

-- A bit disappointing, but mtl does also require m to be a monad...
instance (Monad m) => Applicative (ScopedStateT s m n) where
  pure f = ScopedStateT $ \s -> pure (f, s)
  (<*>) = liftM2 (\f a -> f a)

instance (Monad m) => Monad (ScopedStateT s m n) where
  ScopedStateT m >>= k = ScopedStateT $ m >=> (\ (m', s') -> runScopedStateT (k m') s')

instance (MonadReader r m) => MonadReader r (ScopedStateT s m n) where
  ask = ScopedStateT $ \s -> asks (,s)
  local f m = ScopedStateT (local f . runScopedStateT m)

instance (MonadError e m) => MonadError e (ScopedStateT se m n) where
  throwError e = ScopedStateT $ const (throwError e)
  catchError m k = ScopedStateT $ \s -> runScopedStateT m s `catchError` (\err -> runScopedStateT (k err) s)

instance (MonadWriter w m) => MonadWriter w (ScopedStateT s m n) where
  writer w = ScopedStateT $ \s -> (,s) <$> writer w
  listen m = ScopedStateT $ \s -> (\((m, s'), w) -> ((m, w), s')) <$> listen (runScopedStateT m s)
  pass m = ScopedStateT $ \s -> pass ((\((m, r), s') -> ((m, s'), r)) <$> runScopedStateT m s)

instance (Monad m) => MonadScopedState s (ScopedStateT s m) where
  stateS f = ScopedStateT $ pure . f
  rescope up low m = ScopedStateT $ \s -> do
    (r, s') <- runScopedStateT m (up s)
    return (r, low s')