packages feed

mini-0.1.0.0: Mini/Transformers/EitherT.hs

{- | Extension of a monad with the 'Either' ability to interrupt a sequence of
actions and terminate with a value
-}
module Mini.Transformers.EitherT (
  -- * Type
  EitherT,

  -- * Termination
  left,

  -- * Anticipation
  anticipate,

  -- * Runners
  runEitherT,
) where

import Control.Applicative (
  Alternative (
    empty,
    (<|>)
  ),
 )
import Control.Monad (
  ap,
  liftM,
  (>=>),
 )
import Mini.Transformers.Class (
  MonadTrans (
    lift
  ),
 )

{-
 - Type
 -}

{- | A monad with early termination type /e/, inner monad /m/, and return type
/a/
-}
newtype EitherT e m a = EitherT
  { runEitherT :: m (Either e a)
  -- ^ Unwrap an 'EitherT'
  }

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

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

instance (Monad m, Monoid e) => Alternative (EitherT e m) where
  empty = EitherT . pure $ Left mempty
  m <|> n =
    EitherT $
      runEitherT m
        >>= either
          (\e -> either (Left . mappend e) Right <$> runEitherT n)
          (pure . Right)

instance (Monad m) => Monad (EitherT e m) where
  m >>= k =
    EitherT $
      runEitherT m
        >>= either
          (pure . Left)
          (runEitherT . k)

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

{-
 - Termination
 -}

-- | Terminate an action sequence with the given value
left :: (Monad m) => e -> EitherT e m a
left = EitherT . pure . Left

{-
 - Anticipation
 -}

{- | Run the given action and decide what to do depending on the return type

> anticipate foo >>= either bar baz
-}
anticipate :: (Monad m) => EitherT e m a -> EitherT e m (Either e a)
anticipate = lift . runEitherT . (Right <$>) >=> either (pure . Left) pure