{-# LANGUAGE NoImplicitPrelude #-}
module Nest.EitherT (
    EitherT
  , Except.ExceptT
  , newEitherT
  , runEitherT
  , eitherT
  , mapEitherT
  , bimapEitherT
  , firstEitherT
  , secondEitherT
  , left
  ) where

import           Control.Applicative (Applicative (..))
import           Control.Monad (Monad (..), (>>=))
import qualified Control.Monad.Trans.Except as Except

import           Data.Either (Either (..), either)
import           Data.Function ((.), id)
import           Data.Functor (Functor (..))


type EitherT =
  Except.ExceptT

newEitherT :: f (Either e a) -> EitherT e f a
newEitherT =
  Except.ExceptT

runEitherT :: EitherT e f a -> f (Either e a)
runEitherT =
  Except.runExceptT

eitherT :: Monad m => (x -> m b) -> (a -> m b) -> EitherT x m a -> m b
eitherT f g m =
  runEitherT m >>= either f g

left :: Applicative f => e -> EitherT e f a
left =
  newEitherT . pure . Left

mapEitherT :: (m (Either x a) -> n (Either y b)) -> EitherT x m a -> EitherT y n b
mapEitherT f =
  newEitherT . f . runEitherT

bimapEitherT :: Functor m => (x -> y) -> (a -> b) -> EitherT x m a -> EitherT y m b
bimapEitherT f g =
  mapEitherT (fmap (either (Left . f) (Right . g)))

firstEitherT :: Functor m => (x -> y) -> EitherT x m a -> EitherT y m a
firstEitherT f =
  bimapEitherT f id

secondEitherT :: Functor m => (a -> b) -> EitherT x m a -> EitherT x m b
secondEitherT f =
  bimapEitherT id f