Copyright | (c) 2014 Chris Allen (c) 2014 Edward Kmett (c) 2018-2023 Kowainik (c) 2023 Marco Zocca UnfoldML |
---|---|
License | BSD-3-Clause |
Maintainer | oss@unfoldml.com |
Stability | Stable |
Portability | Portable |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
Lightweight pure data validation based on Applicative
functors.
Validation
allows to accumulate all errors instead of
short-circuting on the first error so you can display all possible
errors at once.
Common use-cases include:
- Validating each input of a form with multiple inputs.
- Performing multiple validations of a single value.
Validation
provides modular and composable interface which
means that you can implement validations for different pieces of your
data independently, and then combine smaller parts into the validation
of a bigger type. The below table illustrates main ways to combine two
Validation
s:
Typeclass | Operation ○ | Failure e ○ Failure d | Success a ○ Success b | Failure e ○ Success a | Success a ○ Failure e |
---|---|---|---|---|---|
Semigroup | <> | Failure (e <> d) | Success (a <> b) | Failure e | Failure e |
Applicative | <*> | Failure (e <> d) | Success (a b) | Failure e | Failure e |
Alternative | <|> | Failure (e <> d) | Success a | Success a | Success a |
In other words, instances of different standard typeclasses provide various semantics which can be useful in different use-cases:
Semigroup
: accumulate bothFailure
andSuccess
with<>
.Monoid
:Success
that storesmempty
.Functor
: change the type insideSuccess
.Bifunctor
: change bothFailure
andSuccess
.Applicative
: apply function to values insideSuccess
and accumulate errors insideFailure
.Alternative
: return the firstSuccess
or accumulate all errors insideFailure
.
Synopsis
- data Validation e a
- isFailure :: Validation e a -> Bool
- isSuccess :: Validation e a -> Bool
- validation :: (e -> x) -> (a -> x) -> Validation e a -> x
- failures :: [Validation e a] -> [e]
- successes :: [Validation e a] -> [a]
- partitionValidations :: [Validation e a] -> ([e], [a])
- fromFailure :: e -> Validation e a -> e
- fromSuccess :: a -> Validation e a -> a
- bindValidation :: Validation e a -> (a -> Validation e b) -> Validation e b
- failure :: e -> Validation (NonEmpty e) a
- failureIf :: Bool -> e -> Validation (NonEmpty e) ()
- failureUnless :: Bool -> e -> Validation (NonEmpty e) ()
- validationToEither :: Validation e a -> Either e a
- eitherToValidation :: Either e a -> Validation e a
- validateAll :: (Foldable f, Semigroup e) => f (a -> Validation e b) -> a -> Validation e a
- whenSuccess :: Applicative f => x -> Validation e a -> (a -> f x) -> f x
- whenFailure :: Applicative f => x -> Validation e a -> (e -> f x) -> f x
- whenSuccess_ :: Applicative f => Validation e a -> (a -> f ()) -> f ()
- whenFailure_ :: Applicative f => Validation e a -> (e -> f ()) -> f ()
- whenSuccessM :: Monad m => x -> m (Validation e a) -> (a -> m x) -> m x
- whenFailureM :: Monad m => x -> m (Validation e a) -> (e -> m x) -> m x
- whenSuccessM_ :: Monad m => m (Validation e a) -> (a -> m ()) -> m ()
- whenFailureM_ :: Monad m => m (Validation e a) -> (e -> m ()) -> m ()
- failureToMaybe :: Validation e a -> Maybe e
- successToMaybe :: Validation e a -> Maybe a
- maybeToFailure :: a -> Maybe e -> Validation e a
- maybeToSuccess :: e -> Maybe a -> Validation e a
Type
data Validation e a Source #
Validation
is a sum type for storing either all
validation failures or validation success. Unlike Either
, which
returns only the first error, Validation
accumulates all errors
using the Semigroup
typeclass.
Usually type variables in
are used as follows:Validation
e a
e
: is a list or set of failure messages or values of some error data type.a
: is some domain type denoting successful validation result.
Some typical use-cases:
Validation
[String
] User- Either list of
String
error messages or a validated value of a customUser
type.
- Either list of
Validation
(NonEmpty
UserValidationError) User- Similar to previous example, but list of failures guaranteed to be non-empty in case of validation failure, and it stores values of some custom error type.
Failure e | Validation failure. The |
Success a | Successful validation result of type |
Instances
Bifoldable Validation Source # | Similar to Examples
|
Defined in Validation.Micro bifold :: Monoid m => Validation m m -> m # bifoldMap :: Monoid m => (a -> m) -> (b -> m) -> Validation a b -> m # bifoldr :: (a -> c -> c) -> (b -> c -> c) -> c -> Validation a b -> c # bifoldl :: (c -> a -> c) -> (c -> b -> c) -> c -> Validation a b -> c # | |
Bifunctor Validation Source # | Similar to Examples
|
Defined in Validation.Micro bimap :: (a -> b) -> (c -> d) -> Validation a c -> Validation b d # first :: (a -> b) -> Validation a c -> Validation b c # second :: (b -> c) -> Validation a b -> Validation a c # | |
Bitraversable Validation Source # | Similar to Examples
|
Defined in Validation.Micro bitraverse :: Applicative f => (a -> f c) -> (b -> f d) -> Validation a b -> f (Validation c d) # | |
NFData2 Validation Source # | |
Defined in Validation.Micro liftRnf2 :: (a -> ()) -> (b -> ()) -> Validation a b -> () # | |
Generic1 (Validation e :: Type -> Type) Source # | |
Defined in Validation.Micro type Rep1 (Validation e) :: k -> Type # from1 :: forall (a :: k). Validation e a -> Rep1 (Validation e) a # to1 :: forall (a :: k). Rep1 (Validation e) a -> Validation e a # | |
Foldable (Validation e) Source # |
Examples
|
Defined in Validation.Micro fold :: Monoid m => Validation e m -> m # foldMap :: Monoid m => (a -> m) -> Validation e a -> m # foldMap' :: Monoid m => (a -> m) -> Validation e a -> m # foldr :: (a -> b -> b) -> b -> Validation e a -> b # foldr' :: (a -> b -> b) -> b -> Validation e a -> b # foldl :: (b -> a -> b) -> b -> Validation e a -> b # foldl' :: (b -> a -> b) -> b -> Validation e a -> b # foldr1 :: (a -> a -> a) -> Validation e a -> a # foldl1 :: (a -> a -> a) -> Validation e a -> a # toList :: Validation e a -> [a] # null :: Validation e a -> Bool # length :: Validation e a -> Int # elem :: Eq a => a -> Validation e a -> Bool # maximum :: Ord a => Validation e a -> a # minimum :: Ord a => Validation e a -> a # sum :: Num a => Validation e a -> a # product :: Num a => Validation e a -> a # | |
Traversable (Validation e) Source # | Traverse values inside Examples
|
Defined in Validation.Micro traverse :: Applicative f => (a -> f b) -> Validation e a -> f (Validation e b) # sequenceA :: Applicative f => Validation e (f a) -> f (Validation e a) # mapM :: Monad m => (a -> m b) -> Validation e a -> m (Validation e b) # sequence :: Monad m => Validation e (m a) -> m (Validation e a) # | |
Monoid e => Alternative (Validation e) Source # | This instance implements the behaviour when the first Examples
|
Defined in Validation.Micro empty :: Validation e a # (<|>) :: Validation e a -> Validation e a -> Validation e a # some :: Validation e a -> Validation e [a] # many :: Validation e a -> Validation e [a] # | |
Semigroup e => Applicative (Validation e) Source # | This instance is the most important instance for the Examples
Implementations of all functions are lazy and they correctly work if some arguments are not fully evaluated.
|
Defined in Validation.Micro pure :: a -> Validation e a # (<*>) :: Validation e (a -> b) -> Validation e a -> Validation e b # liftA2 :: (a -> b -> c) -> Validation e a -> Validation e b -> Validation e c # (*>) :: Validation e a -> Validation e b -> Validation e b # (<*) :: Validation e a -> Validation e b -> Validation e a # | |
Functor (Validation e) Source # | Allows changing the value inside Examples
|
Defined in Validation.Micro fmap :: (a -> b) -> Validation e a -> Validation e b # (<$) :: a -> Validation e b -> Validation e a # | |
NFData e => NFData1 (Validation e) Source # | |
Defined in Validation.Micro liftRnf :: (a -> ()) -> Validation e a -> () # | |
(Data e, Data a) => Data (Validation e a) Source # | |
Defined in Validation.Micro gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> Validation e a -> c (Validation e a) # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c (Validation e a) # toConstr :: Validation e a -> Constr # dataTypeOf :: Validation e a -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c (Validation e a)) # dataCast2 :: Typeable t => (forall d e0. (Data d, Data e0) => c (t d e0)) -> Maybe (c (Validation e a)) # gmapT :: (forall b. Data b => b -> b) -> Validation e a -> Validation e a # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Validation e a -> r # gmapQr :: forall r r'. (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Validation e a -> r # gmapQ :: (forall d. Data d => d -> u) -> Validation e a -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> Validation e a -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> Validation e a -> m (Validation e a) # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> Validation e a -> m (Validation e a) # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> Validation e a -> m (Validation e a) # | |
(Semigroup e, Monoid a) => Monoid (Validation e a) Source # |
Examples
|
Defined in Validation.Micro mempty :: Validation e a # mappend :: Validation e a -> Validation e a -> Validation e a # mconcat :: [Validation e a] -> Validation e a # | |
(Semigroup e, Semigroup a) => Semigroup (Validation e a) Source # |
Examples
|
Defined in Validation.Micro (<>) :: Validation e a -> Validation e a -> Validation e a # sconcat :: NonEmpty (Validation e a) -> Validation e a # stimes :: Integral b => b -> Validation e a -> Validation e a # | |
Generic (Validation e a) Source # | |
Defined in Validation.Micro type Rep (Validation e a) :: Type -> Type # from :: Validation e a -> Rep (Validation e a) x # to :: Rep (Validation e a) x -> Validation e a # | |
(Show e, Show a) => Show (Validation e a) Source # | |
Defined in Validation.Micro showsPrec :: Int -> Validation e a -> ShowS # show :: Validation e a -> String # showList :: [Validation e a] -> ShowS # | |
(NFData e, NFData a) => NFData (Validation e a) Source # | |
Defined in Validation.Micro rnf :: Validation e a -> () # | |
(Eq e, Eq a) => Eq (Validation e a) Source # | |
Defined in Validation.Micro (==) :: Validation e a -> Validation e a -> Bool # (/=) :: Validation e a -> Validation e a -> Bool # | |
(Ord e, Ord a) => Ord (Validation e a) Source # | |
Defined in Validation.Micro compare :: Validation e a -> Validation e a -> Ordering # (<) :: Validation e a -> Validation e a -> Bool # (<=) :: Validation e a -> Validation e a -> Bool # (>) :: Validation e a -> Validation e a -> Bool # (>=) :: Validation e a -> Validation e a -> Bool # max :: Validation e a -> Validation e a -> Validation e a # min :: Validation e a -> Validation e a -> Validation e a # | |
type Rep1 (Validation e :: Type -> Type) Source # | |
Defined in Validation.Micro type Rep1 (Validation e :: Type -> Type) = D1 ('MetaData "Validation" "Validation.Micro" "validation-micro-1.0.0.0-CoEShouOCud9ztgBs5ztFf" 'False) (C1 ('MetaCons "Failure" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 e)) :+: C1 ('MetaCons "Success" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) Par1)) | |
type Rep (Validation e a) Source # | |
Defined in Validation.Micro type Rep (Validation e a) = D1 ('MetaData "Validation" "Validation.Micro" "validation-micro-1.0.0.0-CoEShouOCud9ztgBs5ztFf" 'False) (C1 ('MetaCons "Failure" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 e)) :+: C1 ('MetaCons "Success" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 a))) |
How to use
Interface functions
isFailure :: Validation e a -> Bool Source #
Predicate on if the given Validation
is Failure
.
>>>
isFailure (Failure 'e')
True>>>
isFailure (Success 'a')
False
isSuccess :: Validation e a -> Bool Source #
Predicate on if the given Validation
is Success
.
>>>
isSuccess (Success 'a')
True>>>
isSuccess (Failure 'e')
False
validation :: (e -> x) -> (a -> x) -> Validation e a -> x Source #
Transforms the value of the given Validation
into x
using provided
functions that can transform Failure
and Success
value into the resulting
type respectively.
>>>
let myValidation = validation (<> " world!") (show . (* 10))
>>>
myValidation (Success 100)
"1000">>>
myValidation (Failure "Hello")
"Hello world!"
failures :: [Validation e a] -> [e] Source #
Filters out all Failure
values into the new list of e
s from the given
list of Validation
s.
Note that the order is preserved.
>>>
failures [Failure "Hello", Success 1, Failure "world", Success 2, Failure "!" ]
["Hello","world","!"]
successes :: [Validation e a] -> [a] Source #
Filters out all Success
values into the new list of a
s from the given
list of Validation
s.
Note that the order is preserved.
>>>
successes [Failure "Hello", Success 1, Failure "world", Success 2, Failure "!" ]
[1,2]
partitionValidations :: [Validation e a] -> ([e], [a]) Source #
Redistributes the given list of Validation
s into two lists of e
s and
e
s, where the first list contains all values of Failure
s and the second
one — Success
es correspondingly.
Note that the order is preserved.
>>>
partitionValidations [Failure "Hello", Success 1, Failure "world", Success 2, Failure "!" ]
(["Hello","world","!"],[1,2])
fromFailure :: e -> Validation e a -> e Source #
Returns the contents of a Failure
-value or a default value otherwise.
>>>
fromFailure "default" (Failure "failure")
"failure">>>
fromFailure "default" (Success 1)
"default"
fromSuccess :: a -> Validation e a -> a Source #
Returns the contents of a Success
-value or a default value otherwise.
>>>
fromSuccess 42 (Success 1)
1>>>
fromSuccess 42 (Failure "failure")
42
bindValidation :: Validation e a -> (a -> Validation e b) -> Validation e b Source #
NB Validation is not a monad though
NonEmpty
combinators
When using Validation
, we often work with the NonEmpty
list of errors, and
those lists will be concatenated later.
The following functions aim to help with writing more concise code.
For example, instead of (perfectly fine) code like:
>>>
:{
validateNameVerbose :: String -> Validation (NonEmpty String) String validateNameVerbose name | null name = Failure ("Empty Name" :| []) | otherwise = Success name :}
one can write simply:
>>>
:{
validateNameSimple :: String -> Validation (NonEmpty String) String validateNameSimple name = name <$ failureIf (null name) "Empty Name" :}
failure :: e -> Validation (NonEmpty e) a Source #
failureUnless :: Bool -> e -> Validation (NonEmpty e) () Source #
Returns a Failure
unless the given predicate is True
.
Returns
in case of the predicate is satisfied.Success
()
Similar to failureIf
with the reversed predicate.
failureUnless
p ≡failureIf
(not p)
>>>
let shouldFail = (==) "I am a failure"
>>>
failureUnless (shouldFail "I am a failure") "doesn't matter"
Success ()>>>
failureUnless (shouldFail "I am NOT a failure") "I told you so"
Failure ("I told you so" :| [])
Either
conversion
validationToEither :: Validation e a -> Either e a Source #
Combinators
We are providing several functions for better integration with the Either
related code in this section.
Transform a Validation
into an Either
.
>>>
validationToEither (Success "whoop")
Right "whoop"
>>>
validationToEither (Failure "nahh")
Left "nahh"
eitherToValidation :: Either e a -> Validation e a Source #
Transform an Either
into a Validation
.
>>>
eitherToValidation (Right "whoop")
Success "whoop"
>>>
eitherToValidation (Left "nahh")
Failure "nahh"
Combinators
validateAll :: (Foldable f, Semigroup e) => f (a -> Validation e b) -> a -> Validation e a Source #
Validate all given checks in a Foldable
. Returns the Success
of the
start element when all checks are successful.
A basic example of usage could look like this:
> let validatePassword =validateAll
[ validateEmptyPassword , validateShortPassword ] >validateAll
"VeryStrongPassword"Success
"VeryStrongPassword" >validateAll
""Failure
(EmptyPassword :| [ShortPassword])
When* functions
whenSuccess :: Applicative f => x -> Validation e a -> (a -> f x) -> f x Source #
Applies the given action to Validation
if it is Success
and returns the
result. In case of Failure
the default value is returned.
>>>
whenSuccess "bar" (Failure "foo") (\a -> "success!" <$ print a)
"bar"
>>>
whenSuccess "bar" (Success 42) (\a -> "success!" <$ print a)
42 "success!"
whenFailure :: Applicative f => x -> Validation e a -> (e -> f x) -> f x Source #
Applies the given action to Validation
if it is Failure
and returns the
result. In case of Success
the default value is returned.
>>>
whenFailure "bar" (Failure 42) (\a -> "foo" <$ print a)
42 "foo"
>>>
whenFailure "bar" (Success 42) (\a -> "foo" <$ print a)
"bar"
whenSuccess_ :: Applicative f => Validation e a -> (a -> f ()) -> f () Source #
Applies given action to the Validation
content if it is Success
.
Similar to whenSuccess
but the default value is ()
.
>>>
whenSuccess_ (Failure "foo") print
>>>
whenSuccess_ (Success 42) print
42
whenFailure_ :: Applicative f => Validation e a -> (e -> f ()) -> f () Source #
Applies given action to the Validation
content if it is Failure
.
Similar to whenFailure
but the default value is ()
.
>>>
whenFailure_ (Success 42) putStrLn
>>>
whenFailure_ (Failure "foo") putStrLn
foo
whenSuccessM :: Monad m => x -> m (Validation e a) -> (a -> m x) -> m x Source #
Monadic version of whenSuccess
.
Applies monadic action to the given Validation
in case of Success
.
Returns the resulting value, or provided default.
>>>
whenSuccessM "bar" (pure $ Failure "foo") (\a -> "success!" <$ print a)
"bar"
>>>
whenSuccessM "bar" (pure $ Success 42) (\a -> "success!" <$ print a)
42 "success!"
whenFailureM :: Monad m => x -> m (Validation e a) -> (e -> m x) -> m x Source #
Monadic version of whenFailure
.
Applies monadic action to the given Validation
in case of Failure
.
Returns the resulting value, or provided default.
>>>
whenFailureM "bar" (pure $ Failure 42) (\a -> "foo" <$ print a)
42 "foo"
>>>
whenFailureM "bar" (pure $ Success 42) (\a -> "foo" <$ print a)
"bar"
whenSuccessM_ :: Monad m => m (Validation e a) -> (a -> m ()) -> m () Source #
Monadic version of whenSuccess_
.
Applies monadic action to the given Validation
in case of Success
.
Similar to whenSuccessM
but the default is ()
.
>>>
whenSuccessM_ (pure $ Failure "foo") print
>>>
whenSuccessM_ (pure $ Success 42) print
42
whenFailureM_ :: Monad m => m (Validation e a) -> (e -> m ()) -> m () Source #
Monadic version of whenFailure_
.
Applies monadic action to the given Validation
in case of Failure
.
Similar to whenFailureM
but the default is ()
.
>>>
whenFailureM_ (pure $ Success 42) putStrLn
>>>
whenFailureM_ (pure $ Failure "foo") putStrLn
foo
Maybe
conversion
failureToMaybe :: Validation e a -> Maybe e Source #
Maps Failure
of Validation
to Just
.
>>>
failureToMaybe (Failure True)
Just True>>>
failureToMaybe (Success "aba")
Nothing
successToMaybe :: Validation e a -> Maybe a Source #
Maps Success
of Validation
to Just
.
>>>
successToMaybe (Failure True)
Nothing>>>
successToMaybe (Success "aba")
Just "aba"
maybeToFailure :: a -> Maybe e -> Validation e a Source #
maybeToSuccess :: e -> Maybe a -> Validation e a Source #