{-# LANGUAGE CPP #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE LambdaCase #-}
module Relude.Extra.Validation
( Validation(..)
, validationToEither
, eitherToValidation
) where
import Relude
data Validation e a
= Failure e
| Success a
deriving (Eq, Ord, Show)
instance Functor (Validation e) where
fmap :: (a -> b) -> Validation e a -> Validation e b
fmap _ (Failure e) = Failure e
fmap f (Success a) = Success (f a)
{-# INLINE fmap #-}
(<$) :: a -> Validation e b -> Validation e a
x <$ Success _ = Success x
_ <$ Failure e = Failure e
{-# INLINE (<$) #-}
instance Semigroup e => Applicative (Validation e) where
pure :: a -> Validation e a
pure = Success
{-# INLINE pure #-}
#if MIN_VERSION_base(4,10,0)
liftA2 :: (a -> b -> c) -> Validation e a -> Validation e b -> Validation e c
liftA2 _ (Failure el) (Failure er) = Failure (el <> er)
liftA2 _ (Failure e) (Success _) = Failure e
liftA2 _ (Success _) (Failure e) = Failure e
liftA2 f (Success a) (Success b) = Success (f a b)
{-# INLINE liftA2 #-}
#endif
(<*>) :: Validation e (a -> b) -> Validation e a -> Validation e b
Failure e <*> b = Failure $ case b of
Failure e' -> e <> e'
Success _ -> e
Success _ <*> Failure e = Failure e
Success f <*> Success a = Success (f a)
{-# INLINE (<*>) #-}
(*>) :: Validation e a -> Validation e b -> Validation e b
Failure el *> Failure er = Failure (el <> er)
Failure e *> Success _ = Failure e
Success _ *> Failure e = Failure e
Success _ *> Success b = Success b
{-# INLINE (*>) #-}
(<*) :: Validation e a -> Validation e b -> Validation e a
Failure el <* Failure er = Failure (el <> er)
Failure e <* Success _ = Failure e
Success _ <* Failure e = Failure e
Success a <* Success _ = Success a
{-# INLINE (<*) #-}
instance (Semigroup e, Monoid e) => Alternative (Validation e) where
empty :: Validation e a
empty = Failure mempty
{-# INLINE empty #-}
(<|>) :: Validation e a -> Validation e a -> Validation e a
s@Success{} <|> _ = s
_ <|> s@Success{} = s
Failure e <|> Failure e' = Failure (e <> e')
{-# INLINE (<|>) #-}
instance Foldable (Validation e) where
fold :: Monoid m => Validation e m -> m
fold (Success a) = a
fold (Failure _) = mempty
{-# INLINE fold #-}
foldMap :: Monoid m => (a -> m) -> Validation e a -> m
foldMap _ (Failure _) = mempty
foldMap f (Success a) = f a
{-# INLINE foldMap #-}
foldr :: (a -> b -> b) -> b -> Validation e a -> b
foldr f x (Success a) = f a x
foldr _ x (Failure _) = x
{-# INLINE foldr #-}
instance Traversable (Validation e) where
traverse :: Applicative f => (a -> f b) -> Validation e a -> f (Validation e b)
traverse f (Success a) = Success <$> f a
traverse _ (Failure e) = pure (Failure e)
{-# INLINE traverse #-}
sequenceA :: Applicative f => Validation e (f a) -> f (Validation e a)
sequenceA = traverse id
{-# INLINE sequenceA #-}
instance Bifunctor Validation where
bimap :: (e -> d) -> (a -> b) -> Validation e a -> Validation d b
bimap f _ (Failure e) = Failure (f e)
bimap _ g (Success a) = Success (g a)
{-# INLINE bimap #-}
first :: (e -> d) -> Validation e a -> Validation d a
first f (Failure e) = Failure (f e)
first _ (Success a) = Success a
{-# INLINE first #-}
second :: (a -> b) -> Validation e a -> Validation e b
second _ (Failure e) = Failure e
second g (Success a) = Success (g a)
{-# INLINE second #-}
#if MIN_VERSION_base(4,10,0)
instance Bifoldable Validation where
bifoldMap :: Monoid m => (e -> m) -> (a -> m) -> Validation e a -> m
bifoldMap f _ (Failure e) = f e
bifoldMap _ g (Success a) = g a
{-# INLINE bifoldMap #-}
instance Bitraversable Validation where
bitraverse :: Applicative f
=> (e -> f d) -> (a -> f b) -> Validation e a -> f (Validation d b)
bitraverse f _ (Failure e) = Failure <$> f e
bitraverse _ g (Success a) = Success <$> g a
{-# INLINE bitraverse #-}
#endif
validationToEither :: Validation e a -> Either e a
validationToEither = \case
Failure e -> Left e
Success a -> Right a
{-# INLINE validationToEither #-}
eitherToValidation :: Either e a -> Validation e a
eitherToValidation = \case
Left e -> Failure e
Right a -> Success a
{-# INLINE eitherToValidation #-}