packages feed

validation-1.3.0: src/Data/Validation/ValidationMonad.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wall #-}

-- \$setup
-- >>> import Data.Functor.Identity(Identity(..))
-- >>> import Data.Validation.Validation(Validation(..))
-- >>> import Data.Validation.ValidationMonad
-- >>> import Control.Lens(view, _Wrapped', review, (#), (^?), from)
-- >>> import Data.Functor.Alt(Alt((<!>)))
-- >>> import Data.Functor.Apply(Apply((<.>)))
-- >>> import Data.Functor.Extend(Extend(extended))
-- >>> import Data.Functor.Classes(Eq1(liftEq), Ord1(liftCompare))
-- >>> import Control.Monad.Error.Class(MonadError(throwError, catchError))
-- >>> import Control.Monad.Trans.Class(MonadTrans(lift))
-- >>> import Control.DeepSeq(rnf)
-- >>> import Data.Functor.Plus(Plus(zero))
-- >>> :set -XNoMonomorphismRestriction -w

{- | A monad transformer wrapping @m (Validation err a)@ with short-circuiting
'Applicative' and 'Monad' instances, unlike 'Validation' which accumulates errors.
-}
module Data.Validation.ValidationMonad (
  ValidationMonadT (..),
  ValidationMonad,
  liftValidationMonadT,

  -- * Isomorphisms
  validationMonad,

  -- * Optics

  -- ** Classy lenses
  GetValidationMonadT (..),
  HasValidationMonadT (..),

  -- ** Classy prisms
  ReviewValidationMonadT (..),
  AsValidationMonadT (..),
) where

import Control.Applicative (Alternative (empty, (<|>)))
import Control.DeepSeq (NFData (rnf))
import Control.Lens (Getter, Lens', Prism', Review, Rewrapped, Wrapped (_Wrapped', type Unwrapped), from, prism', unto)
import Control.Lens.Iso (Iso, iso)
import Control.Monad (MonadPlus, ap)
import Control.Monad.Cont.Class (MonadCont (callCC))
import Control.Monad.Error.Class (MonadError (catchError, throwError))
import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad.RWS.Class (MonadRWS)
import Control.Monad.Reader.Class (MonadReader (ask, local, reader))
import Control.Monad.State.Class (MonadState (get, put, state))
import Control.Monad.Trans.Class (MonadTrans (lift))
import Control.Monad.Writer.Class (MonadWriter (listen, pass, tell, writer))
import Control.Selective (Selective (select), selectM)
import qualified Data.Either as Either
import Data.Functor.Alt (Alt ((<!>)))
import Data.Functor.Apply (Apply ((<.>)))
import Data.Functor.Bind (Bind ((>>-)))
import Data.Functor.Bind.Trans (BindTrans (liftB))
import Data.Functor.Classes (Eq1 (liftEq), Ord1 (liftCompare), Show1 (liftShowList, liftShowsPrec))
import Data.Functor.Extend (Extend (extended))
import Data.Functor.Identity (Identity (..))
import Data.Functor.Plus (Plus (zero))
import Data.Validation.Validation (AsValidation (..), GetValidation (..), HasValidation (..), ReviewValidation (..), Validation (..), foldValidation)
import GHC.Generics (Generic)

{- | A monad transformer wrapping @m (Validation err a)@.

>>> ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int
ValidationMonadT (Identity (Success 1))

>>> ValidationMonadT (Identity (Failure "err")) :: ValidationMonadT String Identity Int
ValidationMonadT (Identity (Failure "err"))
-}
newtype ValidationMonadT err m a = ValidationMonadT (m (Validation err a))
  deriving (Generic)

-- | Type alias for @ValidationMonadT err Identity a@.
type ValidationMonad err a = ValidationMonadT err Identity a

{- |
>>> ValidationMonadT (Identity (Success 1)) == (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
True

>>> ValidationMonadT (Identity (Success 1)) == (ValidationMonadT (Identity (Failure "err")) :: ValidationMonadT String Identity Int)
False
-}
deriving instance (Eq (m (Validation err a))) => Eq (ValidationMonadT err m a)

{- |
>>> compare (ValidationMonadT (Identity (Failure "a"))) (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
LT
-}
deriving instance (Ord (m (Validation err a))) => Ord (ValidationMonadT err m a)

{- |
>>> show (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
"ValidationMonadT (Identity (Success 1))"
-}
deriving instance (Show (m (Validation err a))) => Show (ValidationMonadT err m a)

{- |
>>> import Control.Lens(view, _Wrapped')
>>> view _Wrapped' (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
Identity (Success 1)
-}
instance Wrapped (ValidationMonadT err m a) where
  type Unwrapped (ValidationMonadT err m a) = m (Validation err a)
  _Wrapped' = iso (\(ValidationMonadT m) -> m) ValidationMonadT
  {-# INLINE _Wrapped' #-}

instance Rewrapped (ValidationMonadT err m a) (ValidationMonadT err' m' b)

{- |
>>> liftEq (==) (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int) (ValidationMonadT (Identity (Success 1)))
True

>>> liftEq (==) (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int) (ValidationMonadT (Identity (Success 2)))
False
-}
instance (Eq1 m, Eq err) => Eq1 (ValidationMonadT err m) where
  liftEq f (ValidationMonadT ma) (ValidationMonadT mb) = liftEq (liftEq f) ma mb
  {-# INLINE liftEq #-}

{- |
>>> liftCompare compare (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int) (ValidationMonadT (Identity (Success 2)))
LT
-}
instance (Ord1 m, Ord err) => Ord1 (ValidationMonadT err m) where
  liftCompare f (ValidationMonadT ma) (ValidationMonadT mb) = liftCompare (liftCompare f) ma mb
  {-# INLINE liftCompare #-}

instance (Show1 m, Show err) => Show1 (ValidationMonadT err m) where
  liftShowsPrec sp sl d (ValidationMonadT m) =
    showParen (d > 10) $
      showString "ValidationMonadT " . liftShowsPrec (liftShowsPrec sp sl) (liftShowList sp sl) 11 m
  {-# INLINE liftShowsPrec #-}

{- | Lift a value from the base functor into 'ValidationMonadT'.

>>> liftValidationMonadT (Identity 1) :: ValidationMonadT String Identity Int
ValidationMonadT (Identity (Success 1))
-}
liftValidationMonadT :: (Functor m) => m a -> ValidationMonadT err m a
liftValidationMonadT = ValidationMonadT . fmap Success
{-# INLINE liftValidationMonadT #-}

{- |
>>> fmap (+1) (ValidationMonadT (Identity (Success 2)) :: ValidationMonadT String Identity Int)
ValidationMonadT (Identity (Success 3))

>>> fmap (+1) (ValidationMonadT (Identity (Failure "err")) :: ValidationMonadT String Identity Int)
ValidationMonadT (Identity (Failure "err"))
-}
instance (Functor m) => Functor (ValidationMonadT err m) where
  fmap f (ValidationMonadT m) = ValidationMonadT (fmap (fmap f) m)
  {-# INLINE fmap #-}

{- | Short-circuiting: stops at the first 'Failure'.

>>> (ValidationMonadT (Identity (Success (+1))) :: ValidationMonadT String Identity (Int -> Int)) <.> ValidationMonadT (Identity (Success 2))
ValidationMonadT (Identity (Success 3))

>>> (ValidationMonadT (Identity (Failure "e1")) :: ValidationMonadT String Identity (Int -> Int)) <.> ValidationMonadT (Identity (Success 2))
ValidationMonadT (Identity (Failure "e1"))
-}
instance (Monad m) => Apply (ValidationMonadT err m) where
  (<.>) = ap
  {-# INLINE (<.>) #-}

{- | Short-circuiting: unlike 'Validation', does /not/ accumulate errors.

>>> pure 1 :: ValidationMonadT String Identity Int
ValidationMonadT (Identity (Success 1))

>>> (ValidationMonadT (Identity (Failure "e1")) :: ValidationMonadT String Identity (Int -> Int)) <*> (ValidationMonadT (Identity (Failure "e2")) :: ValidationMonadT String Identity Int)
ValidationMonadT (Identity (Failure "e1"))
-}
instance (Monad m) => Applicative (ValidationMonadT err m) where
  pure = ValidationMonadT . pure . Success
  {-# INLINE pure #-}
  ValidationMonadT mf <*> ValidationMonadT ma = ValidationMonadT $ do
    vf <- mf
    case vf of
      Failure e -> pure (Failure e)
      Success f -> fmap (fmap f) ma
  {-# INLINE (<*>) #-}

instance (Monad m) => Bind (ValidationMonadT err m) where
  (>>-) = (>>=)
  {-# INLINE (>>-) #-}

{- | Short-circuiting on the first 'Failure'.

>>> ValidationMonadT (Identity (Success 2)) >>= (\x -> ValidationMonadT (Identity (Success (x + 1)))) :: ValidationMonadT String Identity Int
ValidationMonadT (Identity (Success 3))

>>> (ValidationMonadT (Identity (Failure "err")) :: ValidationMonadT String Identity Int) >>= (\x -> ValidationMonadT (Identity (Success (x + 1))))
ValidationMonadT (Identity (Failure "err"))
-}
instance (Monad m) => Monad (ValidationMonadT err m) where
  ValidationMonadT m >>= k = ValidationMonadT $ do
    va <- m
    case va of
      Failure e -> pure (Failure e)
      Success a -> let ValidationMonadT n = k a in n
  {-# INLINE (>>=) #-}

instance (Monad m, MonadFail m) => MonadFail (ValidationMonadT err m) where
  fail = liftValidationMonadT . fail
  {-# INLINE fail #-}

{- | First 'Success' wins; two 'Failure's accumulate errors.

>>> (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT [String] Identity Int) <!> ValidationMonadT (Identity (Success 2))
ValidationMonadT (Identity (Success 1))

>>> (ValidationMonadT (Identity (Failure ["e1"])) :: ValidationMonadT [String] Identity Int) <!> ValidationMonadT (Identity (Success 2))
ValidationMonadT (Identity (Success 2))

>>> (ValidationMonadT (Identity (Failure ["e1"])) :: ValidationMonadT [String] Identity Int) <!> ValidationMonadT (Identity (Failure ["e2"]))
ValidationMonadT (Identity (Failure ["e1","e2"]))
-}
instance (Monad m, Semigroup err) => Alt (ValidationMonadT err m) where
  ValidationMonadT ma <!> ValidationMonadT mb = ValidationMonadT $ do
    va <- ma
    case va of
      Success a -> pure (Success a)
      Failure e1 -> fmap (foldValidation (Failure . (e1 <>)) Success) mb
  {-# INLINE (<!>) #-}

{- |
>>> zero :: ValidationMonadT [String] Identity Int
ValidationMonadT (Identity (Failure []))
-}
instance (Monad m, Monoid err) => Plus (ValidationMonadT err m) where
  zero = ValidationMonadT (pure (Failure mempty))
  {-# INLINE zero #-}

instance (Monad m, Monoid err) => Alternative (ValidationMonadT err m) where
  empty = zero
  {-# INLINE empty #-}
  (<|>) = (<!>)
  {-# INLINE (<|>) #-}

instance (Monad m, Monoid err) => MonadPlus (ValidationMonadT err m)

instance (Monad m) => Selective (ValidationMonadT err m) where
  select = selectM
  {-# INLINE select #-}

{- |
>>> foldr (:) [] (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
[1]

>>> foldr (:) [] (ValidationMonadT (Identity (Failure "err")) :: ValidationMonadT String Identity Int)
[]
-}
instance (Foldable m) => Foldable (ValidationMonadT err m) where
  foldr f z (ValidationMonadT m) = foldr (flip (foldr f)) z m
  {-# INLINE foldr #-}

{- |
>>> traverse (\x -> [x, x+1]) (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
[ValidationMonadT (Identity (Success 1)),ValidationMonadT (Identity (Success 2))]

>>> traverse (\x -> [x, x+1]) (ValidationMonadT (Identity (Failure "err")) :: ValidationMonadT String Identity Int)
[ValidationMonadT (Identity (Failure "err"))]
-}
instance (Traversable m) => Traversable (ValidationMonadT err m) where
  traverse f (ValidationMonadT m) = ValidationMonadT <$> traverse (traverse f) m
  {-# INLINE traverse #-}

{- |
>>> extended (\_ -> 42) (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int) :: ValidationMonadT String Identity Int
ValidationMonadT (Identity (Success 42))

>>> extended (\_ -> 42) (ValidationMonadT (Identity (Failure "err")) :: ValidationMonadT String Identity Int) :: ValidationMonadT String Identity Int
ValidationMonadT (Identity (Failure "err"))
-}
instance (Functor m) => Extend (ValidationMonadT err m) where
  extended f w@(ValidationMonadT m) = ValidationMonadT (fmap (foldValidation Failure (const (Success (f w)))) m)
  {-# INLINE extended #-}

{- |
>>> (ValidationMonadT (Identity (Failure ["e1"])) :: ValidationMonadT [String] Identity Int) <> ValidationMonadT (Identity (Failure ["e2"]))
ValidationMonadT (Identity (Failure ["e1","e2"]))

>>> (ValidationMonadT (Identity (Failure ["e1"])) :: ValidationMonadT [String] Identity Int) <> ValidationMonadT (Identity (Success 2))
ValidationMonadT (Identity (Success 2))
-}
instance (Applicative m, Semigroup e) => Semigroup (ValidationMonadT e m a) where
  ValidationMonadT ma <> ValidationMonadT mb = ValidationMonadT (liftA2 (<>) ma mb)
  {-# INLINE (<>) #-}

{- |
>>> mempty :: ValidationMonadT [String] Identity Int
ValidationMonadT (Identity (Failure []))
-}
instance (Applicative m, Monoid e) => Monoid (ValidationMonadT e m a) where
  mempty = ValidationMonadT (pure mempty)
  {-# INLINE mempty #-}

{- |
>>> rnf (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
()
-}
instance (NFData (m (Validation err a))) => NFData (ValidationMonadT err m a) where
  rnf (ValidationMonadT m) = rnf m
  {-# INLINE rnf #-}

{- |
>>> lift (Identity 1) :: ValidationMonadT String Identity Int
ValidationMonadT (Identity (Success 1))
-}
instance MonadTrans (ValidationMonadT err) where
  lift = liftValidationMonadT
  {-# INLINE lift #-}

instance BindTrans (ValidationMonadT err) where
  liftB = liftValidationMonadT
  {-# INLINE liftB #-}

{- |
>>> throwError "err" :: ValidationMonadT String Identity Int
ValidationMonadT (Identity (Failure "err"))

>>> catchError (throwError "err" :: ValidationMonadT String Identity Int) (\e -> pure (length e))
ValidationMonadT (Identity (Success 3))
-}
instance (Monad m) => MonadError err (ValidationMonadT err m) where
  throwError = ValidationMonadT . pure . Failure
  {-# INLINE throwError #-}
  catchError (ValidationMonadT m) h = ValidationMonadT $ do
    va <- m
    case va of
      Failure e -> let ValidationMonadT n = h e in n
      Success a -> pure (Success a)
  {-# INLINE catchError #-}

instance (MonadIO m) => MonadIO (ValidationMonadT err m) where
  liftIO = liftValidationMonadT . liftIO
  {-# INLINE liftIO #-}

instance (MonadReader r m) => MonadReader r (ValidationMonadT err m) where
  ask = liftValidationMonadT ask
  {-# INLINE ask #-}
  local f (ValidationMonadT m) = ValidationMonadT (local f m)
  {-# INLINE local #-}
  reader = liftValidationMonadT . reader
  {-# INLINE reader #-}

instance (MonadWriter w m) => MonadWriter w (ValidationMonadT err m) where
  writer = liftValidationMonadT . writer
  {-# INLINE writer #-}
  tell = liftValidationMonadT . tell
  {-# INLINE tell #-}
  listen (ValidationMonadT m) = ValidationMonadT $ do
    (va, w) <- listen m
    pure (fmap (,w) va)
  {-# INLINE listen #-}
  pass (ValidationMonadT m) = ValidationMonadT $ pass $ do
    va <- m
    pure $ case va of
      Failure e -> (Failure e, id)
      Success (a, f) -> (Success a, f)
  {-# INLINE pass #-}

instance (MonadState s m) => MonadState s (ValidationMonadT err m) where
  get = liftValidationMonadT get
  {-# INLINE get #-}
  put = liftValidationMonadT . put
  {-# INLINE put #-}
  state = liftValidationMonadT . state
  {-# INLINE state #-}

instance (MonadCont m) => MonadCont (ValidationMonadT err m) where
  callCC f = ValidationMonadT $ callCC $ \c ->
    let ValidationMonadT m = f (ValidationMonadT . c . Success) in m
  {-# INLINE callCC #-}

instance (MonadRWS r w s m) => MonadRWS r w s (ValidationMonadT err m)

{- | Class for types that have a 'Getter' to a 'ValidationMonadT'.

>>> import Control.Lens(view)
>>> view getValidationMonadT (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
ValidationMonadT (Identity (Success 1))
-}
class GetValidationMonadT s err m a | s -> err m a where
  getValidationMonadT :: Getter s (ValidationMonadT err m a)

instance GetValidationMonadT (ValidationMonadT err m a) err m a where
  getValidationMonadT = id
  {-# INLINE getValidationMonadT #-}

{- | Class for types that have a 'Lens'' to a 'ValidationMonadT'.

>>> import Control.Lens(view)
>>> view validationMonadT (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
ValidationMonadT (Identity (Success 1))
-}
class (GetValidationMonadT s err m a) => HasValidationMonadT s err m a | s -> err m a where
  validationMonadT :: Lens' s (ValidationMonadT err m a)

instance HasValidationMonadT (ValidationMonadT err m a) err m a where
  validationMonadT = id
  {-# INLINE validationMonadT #-}

{- | Class for types that have a 'Review' to a 'ValidationMonadT'.

>>> import Control.Lens(review)
>>> review reviewValidationMonadT (ValidationMonadT (Identity (Success 1))) :: ValidationMonadT String Identity Int
ValidationMonadT (Identity (Success 1))
-}
class ReviewValidationMonadT s err m a | s -> err m a where
  reviewValidationMonadT :: Review s (ValidationMonadT err m a)

instance ReviewValidationMonadT (ValidationMonadT err m a) err m a where
  reviewValidationMonadT = unto id
  {-# INLINE reviewValidationMonadT #-}

-- | Class for types that have a 'Prism'' to a 'ValidationMonadT'.
class (ReviewValidationMonadT s err m a) => AsValidationMonadT s err m a | s -> err m a where
  _ValidationMonadT :: Prism' s (ValidationMonadT err m a)

instance AsValidationMonadT (ValidationMonadT err m a) err m a where
  _ValidationMonadT = id
  {-# INLINE _ValidationMonadT #-}

{- | Isomorphism between @Validation err a@ and @ValidationMonadT err Identity a@.

>>> import Control.Lens(view, from)
>>> view validationMonad (Success 1 :: Validation String Int)
ValidationMonadT (Identity (Success 1))

>>> view (from validationMonad) (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
Success 1
-}
validationMonad :: Iso (Validation err a) (Validation err' a') (ValidationMonad err a) (ValidationMonad err' a')
validationMonad = iso (ValidationMonadT . pure) (\(ValidationMonadT (Identity v)) -> v)
{-# INLINE validationMonad #-}

{- |
>>> import Control.Lens(view)
>>> view getValidationMonadT (Success 1 :: Validation String Int)
ValidationMonadT (Identity (Success 1))
-}
instance GetValidationMonadT (Validation err a) err Identity a where
  getValidationMonadT = validationMonad
  {-# INLINE getValidationMonadT #-}

{- |
>>> import Control.Lens(view)
>>> view validationMonadT (Success 1 :: Validation String Int)
ValidationMonadT (Identity (Success 1))
-}
instance HasValidationMonadT (Validation err a) err Identity a where
  validationMonadT = validationMonad
  {-# INLINE validationMonadT #-}

{- |
>>> import Control.Lens(review)
>>> review reviewValidationMonadT (ValidationMonadT (Identity (Success 1))) :: Validation String Int
Success 1
-}
instance ReviewValidationMonadT (Validation err a) err Identity a where
  reviewValidationMonadT = unto (\(ValidationMonadT (Identity v)) -> v)
  {-# INLINE reviewValidationMonadT #-}

{- |
>>> import Control.Lens((^?))
>>> (Success 1 :: Validation String Int) ^? _ValidationMonadT
Just (ValidationMonadT (Identity (Success 1)))
-}
instance AsValidationMonadT (Validation err a) err Identity a where
  _ValidationMonadT =
    prism'
      (\(ValidationMonadT (Identity v)) -> v)
      (Just . ValidationMonadT . pure)
  {-# INLINE _ValidationMonadT #-}

{- |
>>> import Control.Lens(view)
>>> view getValidation (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
Success 1
-}
instance GetValidation (ValidationMonad err a) err a where
  getValidation = from validationMonad
  {-# INLINE getValidation #-}

{- |
>>> import Control.Lens(view)
>>> view validation (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int)
Success 1
-}
instance HasValidation (ValidationMonad err a) err a where
  validation = from validationMonad
  {-# INLINE validation #-}

instance ReviewValidation (ValidationMonad err a) err a where
  reviewValidation = unto (ValidationMonadT . Identity)
  {-# INLINE reviewValidation #-}

{- |
>>> import Control.Lens((^?))
>>> (ValidationMonadT (Identity (Success 1)) :: ValidationMonadT String Identity Int) ^? _Validation
Just (Success 1)
-}
instance AsValidation (ValidationMonad err a) err a where
  _Validation = from validationMonad
  {-# INLINE _Validation #-}

{- |
>>> import Control.Lens(view)
>>> view getValidationMonadT (Left "err" :: Either String Int)
ValidationMonadT (Identity (Failure "err"))

>>> view getValidationMonadT (Right 1 :: Either String Int)
ValidationMonadT (Identity (Success 1))
-}
instance GetValidationMonadT (Either err a) err Identity a where
  getValidationMonadT = iso (ValidationMonadT . Identity . Either.either Failure Success) (\(ValidationMonadT (Identity v)) -> foldValidation Left Right v)
  {-# INLINE getValidationMonadT #-}

{- |
>>> import Control.Lens(view, set)
>>> view validationMonadT (Left "err" :: Either String Int)
ValidationMonadT (Identity (Failure "err"))

>>> set validationMonadT (ValidationMonadT (Identity (Success 2))) (Left "err" :: Either String Int)
Right 2
-}
instance HasValidationMonadT (Either err a) err Identity a where
  validationMonadT = iso (ValidationMonadT . Identity . Either.either Failure Success) (\(ValidationMonadT (Identity v)) -> foldValidation Left Right v)
  {-# INLINE validationMonadT #-}

{- |
>>> import Control.Lens(review)
>>> review reviewValidationMonadT (ValidationMonadT (Identity (Success 1))) :: Either String Int
Right 1

>>> review reviewValidationMonadT (ValidationMonadT (Identity (Failure "err"))) :: Either String Int
Left "err"
-}
instance ReviewValidationMonadT (Either err a) err Identity a where
  reviewValidationMonadT = unto (\(ValidationMonadT (Identity v)) -> foldValidation Left Right v)
  {-# INLINE reviewValidationMonadT #-}

{- |
>>> import Control.Lens((^?))
>>> (Left "err" :: Either String Int) ^? _ValidationMonadT
Just (ValidationMonadT (Identity (Failure "err")))

>>> (Right 1 :: Either String Int) ^? _ValidationMonadT
Just (ValidationMonadT (Identity (Success 1)))
-}
instance AsValidationMonadT (Either err a) err Identity a where
  _ValidationMonadT = iso (ValidationMonadT . Identity . Either.either Failure Success) (\(ValidationMonadT (Identity v)) -> foldValidation Left Right v)
  {-# INLINE _ValidationMonadT #-}