{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

{- | Monad transformer providing 'MonadFail'. -}
module OM.Fail (
  FailT,
  runFailT,
) where


import Control.Exception.Safe (MonadThrow, MonadCatch)
import Control.Monad.Fail (MonadFail, fail)
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Logger (MonadLogger, MonadLoggerIO)
import Control.Monad.Trans.Class (MonadTrans)
import Control.Monad.Trans.Except (ExceptT, runExceptT, throwE)


{- | Monad transformer providing 'MonadFail'. -}
newtype FailT m a = FailT {
    unFailT :: ExceptT String m a
  }
  deriving newtype (
    Functor, Applicative, Monad, MonadIO, MonadLogger, MonadLoggerIO,
    MonadThrow, MonadCatch, MonadTrans
  )
instance (Monad m) => MonadFail (FailT m) where
  fail = FailT . throwE


{- | Run a 'FailT'. -}
runFailT :: FailT m a -> m (Either String a)
runFailT = runExceptT . unFailT