module Data.Validated import Data.List1 import Decidable.Equality %default total ||| `Validated` is like an `Either` but accumulates all errors with semigroup operation. public export data Validated e a = Valid a | Invalid e --- Instances of standard interfaces --- public export (Eq e, Eq a) => Eq (Validated e a) where Valid x == Valid y = x == y Invalid e == Invalid f = e == f _ == _ = False export (Show e, Show a) => Show (Validated e a) where showPrec d $ Valid x = showCon d "Valid" $ showArg x showPrec d $ Invalid e = showCon d "Invalid" $ showArg e public export Functor (Validated e) where map f $ Valid x = Valid $ f x map _ $ Invalid e = Invalid e public export Bifunctor Validated where bimap _ s $ Valid x = Valid $ s x bimap f _ $ Invalid e = Invalid $ f e public export Bifoldable Validated where bifoldr _ g acc (Valid a) = g a acc bifoldr f _ acc (Invalid e) = f e acc bifoldl _ g acc (Valid a) = g acc a bifoldl f _ acc (Invalid e) = f acc e binull _ = False public export Bitraversable Validated where bitraverse _ g (Valid a) = Valid <$> g a bitraverse f _ (Invalid e) = Invalid <$> f e ||| Applicative composition preserves invalidity sequentially accumulating all errors. public export Semigroup e => Applicative (Validated e) where pure = Valid Valid f <*> Valid x = Valid $ f x Invalid e1 <*> Invalid e2 = Invalid $ e1 <+> e2 Invalid e <*> Valid _ = Invalid e Valid _ <*> Invalid e = Invalid e -- There is no `Monad` implementation because it can't be coherent with the accumulating `Applicative` one. ||| Semigroup operation selects the leftmost valid value. ||| If both sides are invalid, errors are accumulated. public export Semigroup e => Semigroup (Validated e a) where l@(Valid _) <+> _ = l _ <+> r@(Valid _) = r Invalid e1 <+> Invalid e2 = Invalid $ e1 <+> e2 public export Monoid e => Monoid (Validated e a) where neutral = Invalid neutral ||| Alternative composition preserves validity selecting the leftmost valid value. ||| If both sides are invalid, errors are accumulated. public export Monoid e => Alternative (Validated e) where empty = neutral l@(Valid _) <|> _ = l _ <|> r@(Valid _) = r Invalid e1 <|> Invalid e2 = Invalid $ e1 <+> e2 public export Foldable (Validated e) where foldr op init $ Valid x = x `op` init foldr _ init $ Invalid _ = init foldl op init $ Valid x = init `op` x foldl _ init $ Invalid _ = init null $ Valid _ = False null $ Invalid _ = True public export Traversable (Validated e) where traverse f $ Valid x = Valid <$> f x traverse _ $ Invalid e = pure $ Invalid e public export Semigroup e => Zippable (Validated e) where zipWith f (Valid l) (Valid r) = Valid $ f l r zipWith _ (Valid _) (Invalid r) = Invalid r zipWith _ (Invalid l) (Valid _) = Invalid l zipWith _ (Invalid l) (Invalid r) = Invalid $ l <+> r zipWith3 f (Valid x) (Valid y) (Valid z) = Valid $ f x y z zipWith3 _ (Valid _) (Valid _) (Invalid z) = Invalid z zipWith3 _ (Valid _) (Invalid y) (Valid _) = Invalid y zipWith3 _ (Valid _) (Invalid y) (Invalid z) = Invalid $ y <+> z zipWith3 _ (Invalid x) (Valid _) (Valid _) = Invalid x zipWith3 _ (Invalid x) (Valid _) (Invalid z) = Invalid $ x <+> z zipWith3 _ (Invalid x) (Invalid y) (Valid _) = Invalid $ x <+> y zipWith3 _ (Invalid x) (Invalid y) (Invalid z) = Invalid $ x <+> y <+> z unzipWith f (Valid x) = let (a, b) = f x in (Valid a, Valid b) unzipWith _ (Invalid e) = (Invalid e, Invalid e) unzipWith3 f (Valid x) = let (a, b, c) = f x in (Valid a, Valid b, Valid c) unzipWith3 _ (Invalid e) = (Invalid e, Invalid e, Invalid e) public export Uninhabited (Valid x = Invalid e) where uninhabited Refl impossible public export Uninhabited (Invalid e = Valid x) where uninhabited Refl impossible public export (DecEq e, DecEq a) => DecEq (Validated e a) where decEq (Valid x) (Invalid y) = No uninhabited decEq (Invalid x) (Valid y) = No uninhabited decEq (Valid x) (Valid y) with (decEq x y) decEq (Valid _) (Valid _) | Yes p = rewrite p in Yes Refl decEq (Valid _) (Valid _) | No up = No $ \case Refl => up Refl decEq (Invalid x) (Invalid y) with (decEq x y) decEq (Invalid _) (Invalid _) | Yes p = rewrite p in Yes Refl decEq (Invalid _) (Invalid _) | No up = No $ \case Refl => up Refl --- Convenience representations --- ||| Special case of `Validated` with a `List` as an error accumulator. public export %inline ValidatedL : Type -> Type -> Type ValidatedL = Validated . List1 public export %inline oneInvalid : Applicative f => e -> Validated (f e) a oneInvalid = Invalid . pure --- Conversions to and from `Either` --- public export %inline fromEither : Either e a -> Validated e a fromEither $ Right x = Valid x fromEither $ Left e = Invalid e public export %inline fromEitherL : Either e a -> ValidatedL e a fromEitherL $ Right x = Valid x fromEitherL $ Left e = oneInvalid e public export %inline toEither : Validated e a -> Either e a toEither $ Valid x = Right x toEither $ Invalid e = Left e --- Conversions to and from `Maybe` --- public export %inline fromMaybe : Monoid e => Maybe a -> Validated e a fromMaybe $ Just x = Valid x fromMaybe $ Nothing = empty public export %inline toMaybe : Validated e a -> Maybe a toMaybe $ Valid x = Just x toMaybe $ Invalid _ = Nothing --- Property of being valid --- public export data IsValid : Validated e a -> Type where ItIsValid : IsValid $ Valid x export Uninhabited (IsValid $ Invalid e) where uninhabited ItIsValid impossible public export isItValid : (v : Validated e a) -> Dec (IsValid v) isItValid $ Valid _ = Yes ItIsValid isItValid $ Invalid _ = No absurd