packages feed

mini-1.7.0.0: src/Mini/Transformers/EitherT.hs

-- | Extend a monad with the ability to terminate a computation with a value
module Mini.Transformers.EitherT (
  -- * Type
  EitherT (
    EitherT
  ),
  runEitherT,

  -- * Operations
  eitherT,
  left,
  right,
) where

import Control.Applicative (
  Alternative,
  empty,
  (<|>),
 )
import Control.Monad (
  ap,
  liftM,
 )
import Control.Monad.IO.Class (
  MonadIO,
  liftIO,
 )
import Mini.Transformers.Class (
  MonadTrans,
  lift,
 )
import Prelude (
  Applicative,
  Either (
    Left,
    Right
  ),
  Functor,
  Monad,
  MonadFail,
  Monoid,
  either,
  fail,
  fmap,
  mappend,
  mempty,
  pure,
  ($),
  (.),
  (<*>),
  (>>=),
 )

-- Type

-- | A terminable transformer with termination /e/, inner monad /m/, return /a/
newtype EitherT e m a = EitherT
  { runEitherT :: m (Either e a)
  -- ^ Unwrap a transformer computation
  }

instance (Monad m) => Functor (EitherT e m) where
  fmap = liftM

instance (Monad m) => Applicative (EitherT e m) where
  pure = right
  (<*>) = ap

instance (Monad m, Monoid e) => Alternative (EitherT e m) where
  empty = left mempty
  m <|> n = eitherT (\e -> eitherT (left . mappend e) right n) right m

instance (Monad m) => Monad (EitherT e m) where
  m >>= k = eitherT left k m

instance MonadTrans (EitherT e) where
  lift = EitherT . fmap Right

instance (MonadFail m) => MonadFail (EitherT e m) where
  fail = EitherT . fail

instance (MonadIO m) => MonadIO (EitherT e m) where
  liftIO = lift . liftIO

-- Operations

-- | Case analysis on the result of a computation
eitherT
  :: (Monad m)
  => (e -> EitherT e' m b) -- ^ Function applied in case of @Left e@
  -> (a -> EitherT e' m b) -- ^ Function applied in case of @Right a@
  -> EitherT e m a -- ^ Object of the case analysis
  -> EitherT e' m b
eitherT l r m = EitherT $ runEitherT m >>= runEitherT . either l r

-- | Terminate the computation with a value
left :: (Applicative m) => e -> EitherT e m a
left = EitherT . pure . Left

-- | Return a value
right :: (Applicative m) => a -> EitherT e m a
right = EitherT . pure . Right