HORIZON HASKELLDocslts/ghc-9.10.xc74966e2026-09-27Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · c74966e · 2026-09-27

Modulevalidation-selective-0.2.0.0Haskell2010

Validation

SPDX-License-Identifier: MPL-2.0 Maintainer: Kowainik xrom.xkov@gmail.com Stability: Stable Portability: Portable

Lightweight pure data validation based on Applicative and Selective 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:

  1. Validating each input of a form with multiple inputs.

  2. 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 Validations:

In other words, instances of different standard typeclasses provide various semantics which can be useful in different use-cases:

  1. Semigroup: accumulate both Failure and Success with <>.

  2. Monoid: Success that stores mempty.

  3. Functor: change the type inside Success.

  4. Bifunctor: change both Failure and Success.

  5. Applicative: apply function to values inside Success and accumulate errors inside Failure.

  6. Alternative: return the first Success or accumulate all errors inside Failure.

  7. Selective: choose which validations to apply based on the value inside.

  • 1 type
  • 26 values

Type

1 declaration
datadata Validation e a
#

Validation is a polymorphic 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 Validation e a are used as follows:

  • 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 custom User type.

  • 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.

Constructors

  • Failure e

    Validation failure. The e type is supposed to implement the Semigroup instance.

  • Success a

    Successful validation result of type a.

Instances23Bifoldable, Bifunctor, Bitraversable, NFData2, Generic1, Monad, …
  • Bifoldable ValidationDefined in validation-selective-0.2.0.0 · Validation

    Similar to Foldable but allows folding both Failure and Success to the same monoidal value according to given functions.

    Examples

    Example3 expressions
    one x = [x]bifoldMap id (one . show) (Success 15)["15"]bifoldMap id (one . show) (Failure ["Wrong", "Fail"])["Wrong","Fail"]
  • Bifunctor ValidationDefined in validation-selective-0.2.0.0 · Validation

    Similar to Functor but allows mapping of values inside both Failure and Success.

    Examples

    Example2 expressions
    bimap length show (Success 50)Success "50"bimap length show (Failure ["15", "9"])Failure 2
  • Bitraversable ValidationDefined in validation-selective-0.2.0.0 · Validation

    Similar to Traversable but traverses both Failure and Success with given effectful computations.

    Examples

    Example5 expressions
    parseInt = readMaybe :: String -> Maybe Intbitraverse listToMaybe parseInt (Success "42")Just (Success 42)bitraverse listToMaybe parseInt (Success "int")Nothingbitraverse listToMaybe parseInt (Failure [15])Just (Failure 15)bitraverse listToMaybe parseInt (Failure [])Nothing
  • NFData2 ValidationDefined in validation-selective-0.2.0.0 · Validation
  • Generic1 (Validation e)Defined in validation-selective-0.2.0.0 · Validation
  • (NoValidationMonadError, Semigroup e) => Monad (Validation e)Defined in validation-selective-0.2.0.0 · Validation

    ⚠️CAUTION⚠️ This instance is for custom error display only.

    It's not possible to implement lawful Monad instance for Validation.

    In case it is used by mistake, the user will see the following:

    Example1 expression
    Success 42 >>= \n -> if even n then Success n else Failure ["Not even"]...... Type 'Validation' doesn't have lawful 'Monad' instance      which means that you can't use 'Monad' methods with 'Validation'....
  • Functor (Validation e)Defined in validation-selective-0.2.0.0 · Validation

    Allows changing the value inside Success with a given function.

    Examples

    Example2 expressions
    fmap (+1) (Success 9)Success 10fmap (+1) (Failure ["wrong"])Failure ["wrong"]
  • Semigroup e => Applicative (Validation e)Defined in validation-selective-0.2.0.0 · Validation

    This instance is the most important instance for the Validation data type. It's responsible for the many implementations. And it allows to accumulate errors while performing validation or combining the results in the applicative style.

    Examples

    Example5 expressions
    success1 = Success 9 :: Validation [String] Intsuccess2 = Success 15 :: Validation [String] IntsuccessF = Success (* 2) :: Validation [String] (Int -> Int)failure1 = Failure ["WRONG"] :: Validation [String] Intfailure2 = Failure ["FAIL"]  :: Validation [String] Int
    Example6 expressions
    successF <*> success1Success 18successF <*> failure1Failure ["WRONG"](+) <$> success1 <*> success2Success 24(+) <$> failure1 <*> failure2Failure ["WRONG","FAIL"]liftA2 (+) success1 failure1Failure ["WRONG"]liftA3 (,,) failure1 success1 failure2Failure ["WRONG","FAIL"]

    Implementations of all functions are lazy and they correctly work if some arguments are not fully evaluated.

    Example4 expressions
    failure1 *> failure2Failure ["WRONG","FAIL"]isFailure $ failure1 *> failure2TrueepicFail = error "Impossible validation" :: Validation [String] IntisFailure $ failure1 *> epicFailTrue
  • Foldable (Validation e)Defined in validation-selective-0.2.0.0 · Validation

    Foldable for Validation allows folding values inside Success.

    Examples

    Example2 expressions
    fold (Success [16])[16]fold (Failure "WRONG!" :: Validation String [Int])[]
  • Traversable (Validation e)Defined in validation-selective-0.2.0.0 · Validation

    Traverse values inside Success with some effectful computation.

    Examples

    Example4 expressions
    parseInt = readMaybe :: String -> Maybe Inttraverse parseInt (Success "42")Just (Success 42)traverse parseInt (Success "int")Nothingtraverse parseInt (Failure ["42"])Just (Failure ["42"])
  • Monoid e => Alternative (Validation e)Defined in validation-selective-0.2.0.0 · Validation

    This instance implements the behaviour when the first Success is returned. Otherwise all Failures are combined.

    Examples

    Example4 expressions
    success1 = Success [9] :: Validation [String] [Int]success2 = Success [15] :: Validation [String] [Int]failure1 = Failure ["WRONG"] :: Validation [String] [Int]failure2 = Failure ["FAIL"]  :: Validation [String] [Int]
    Example3 expressions
    success1 <|> success2Success [9]failure1 <|> failure2Failure ["WRONG","FAIL"]failure2 <|> success2Success [15]
  • NFData e => NFData1 (Validation e)Defined in validation-selective-0.2.0.0 · Validation
  • Semigroup e => Selective (Validation e)Defined in validation-selective-0.2.0.0 · Validation

    Selective functors from the selective package. This instance allows choosing which validations to apply based on value inside. Validation can't have a lawful Monad instance but it's highly desirable to have the monadic behavior in cases when you want future checks depend on previous values. Selective allows to circumvent this limitation by providing the desired behavior.

    Examples

    To understand better, how Selective can be helpful, let's consider a typical usage example with validating passwords.

    Example1 expression
    :{newtype Password = Password    { unPassword :: String    } deriving stock (Show):}

    When user enters a password in some form, we want to check the following conditions:

    1. Password must not be empty.

    2. Password must contain at least 8 characters.

    3. Password must contain at least 1 digit.

    As in the previous usage example with form validation, let's introduce a custom data type to represent all possible errors.

    Example1 expression
    :{data PasswordValidationError    = EmptyPassword    | ShortPassword    | NoDigitPassword    deriving stock (Show):}

    And, again, we can implement independent functions to validate all these cases:

    Example1 expression
    type PasswordValidation = Validation (NonEmpty PasswordValidationError) Password
    Example1 expression
    :{validateEmptyPassword :: String -> PasswordValidationvalidateEmptyPassword password = Password password <$    failureIf (null password) EmptyPassword:}
    Example1 expression
    :{validateShortPassword :: String -> PasswordValidationvalidateShortPassword password = Password password <$    failureIf (length password < 8) ShortPassword:}
    Example1 expression
    :{validatePasswordDigit :: String -> PasswordValidationvalidatePasswordDigit password = Password password <$    failureUnless (any isDigit password) NoDigitPassword:}

    And we can easily compose all these checks into single validation for Password using Applicative instance:

    Example1 expression
    :{validatePassword :: String -> PasswordValidationvalidatePassword password =    validateEmptyPassword password    *> validateShortPassword password    *> validatePasswordDigit password:}

    However, if we try using this function, we can notice a problem immediately:

    Example1 expression
    validatePassword ""Failure (EmptyPassword :| [ShortPassword,NoDigitPassword])

    Due to the nature of the Applicative instance for Validation, we run all checks and combine all possible errors. But you can notice that if password is empty, it doesn't make sense to run other validations. The fact that the password is empty implies that password is shorter than 8 characters.

    You may say that check for empty password is redundant because empty password is a special case of a short password. However, when using Validation, we want to display readable and friendly errors to users, so they know how to fix errors and can act correspondingly.

    This behaviour could be achieved easily if Validation had the Monad instance. But it can't have a lawful Monad instance. Fortunately, the Selective instance for Validation can help with our problem. But to solve it, we need to write our password validation in a slightly different way.

    First, we need to write a function that checks whether the password is empty:

    Example1 expression
    :{checkEmptyPassword :: String -> Validation e BoolcheckEmptyPassword = Success . null:}

    Now we can use the ifS function from the selective package to branch on the result of checkEmptyPassword:

    Example1 expression
    :{validatePassword :: String -> PasswordValidationvalidatePassword password = ifS    (checkEmptyPassword password)    (failure EmptyPassword)    (validateShortPassword password *> validatePasswordDigit password):}

    With this implementation we achieved our desired behavior:

    Example4 expressions
    validatePassword ""Failure (EmptyPassword :| [])validatePassword "abc"Failure (ShortPassword :| [NoDigitPassword])validatePassword "abc123"Failure (ShortPassword :| [])validatePassword "security567"Success (Password {unPassword = "security567"})
  • (Eq e, Eq a) => Eq (Validation e a)Defined in validation-selective-0.2.0.0 · Validation
  • (Data e, Data a) => Data (Validation e a)Defined in validation-selective-0.2.0.0 · Validation
  • (Ord e, Ord a) => Ord (Validation e a)Defined in validation-selective-0.2.0.0 · Validation
  • (Show e, Show a) => Show (Validation e a)Defined in validation-selective-0.2.0.0 · Validation
  • Generic (Validation e a)Defined in validation-selective-0.2.0.0 · Validation
  • (Semigroup e, Semigroup a) => Semigroup (Validation e a)Defined in validation-selective-0.2.0.0 · Validation

    Semigroup allows merging multiple Validations into single one by combining values inside both Failure and Success. The <> operator merges two Validations following the below rules:

    1. If both values are Failures, returns a new Failure with accumulated errors.

    2. If both values are Successful, returns a new Success with combined success using Semigroup for values inside Success.

    3. If one value is Failure and another one is Success, then Failure is returned.

    Examples

    Example4 expressions
    success1 = Success [9] :: Validation [String] [Int]success2 = Success [15] :: Validation [String] [Int]failure1 = Failure ["WRONG"] :: Validation [String] [Int]failure2 = Failure ["FAIL"]  :: Validation [String] [Int]
    Example4 expressions
    success1 <> success2Success [9,15]failure1 <> failure2Failure ["WRONG","FAIL"]success1 <> failure1Failure ["WRONG"]failure2 <> success1 <> success2 <> failure1Failure ["FAIL","WRONG"]
  • (Semigroup e, Monoid a) => Monoid (Validation e a)Defined in validation-selective-0.2.0.0 · Validation

    mempty :: Validation e a is Success which stores mempty :: a to be consistent with the Semigroup instance.

    Examples

    Example1 expression
    mempty :: Validation String [Bool]Success []
  • (NFData e, NFData a) => NFData (Validation e a)Defined in validation-selective-0.2.0.0 · Validation
  • type Rep (Validation e a) = D1 ('MetaData "Validation" "Validation" "validation-selective-0.2.0.0-BLtLhIrZ39o5Jzj6FfbGYk" 'False) (C1 ('MetaCons "Failure" 'PrefixI 'False) (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 e)) :+: C1 ('MetaCons "Success" 'PrefixI 'False) (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 a)))Defined in validation-selective-0.2.0.0 · Validation
  • type Rep1 (Validation e) = D1 ('MetaData "Validation" "Validation" "validation-selective-0.2.0.0-BLtLhIrZ39o5Jzj6FfbGYk" 'False) (C1 ('MetaCons "Failure" 'PrefixI 'False) (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 e)) :+: C1 ('MetaCons "Success" 'PrefixI 'False) (S1 ('MetaSel 'Nothing 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) Par1))Defined in validation-selective-0.2.0.0 · Validation

How to use

0 declarations

This section contains the typical Validation usage example. Let's say we have a form with fields where you can input your login information.

Example1 expression
:{data Form = Form    { formUserName :: !String    , formPassword :: !String    }:}

This Form data type can represent values of some text fields on the web page or inside the GUI application. Our goal is to create a value of the custom User data type from the Form fields.

First, let's define our User type and additional newtypes for more type safety.

Example1 expression
:{newtype UserName = UserName    { unUserName :: String    } deriving newtype (Show):}
Example1 expression
:{newtype Password = Password    { unPassword :: String    } deriving newtype (Show):}
Example1 expression
:{data User = User    { userName     :: !UserName    , userPassword :: !Password    } deriving stock (Show):}

We can easily create a User from the Form in the unsafe way by wrapping each form field into the corresponding newtype:

Example1 expression
:{unsafeUserFromForm :: Form -> UserunsafeUserFromForm Form{..} = User    { userName     = UserName formUserName    , userPassword = Password formPassword    }:}

However, this conversion is unsafe (as name suggests) since Form can contain invalid data. So, before creating a User we want to check whether all Form fields satisfy our preconditions. Specifically:

  1. User name must not be empty.

  2. Password should be at least 8 characters long.

  3. Password should contain at least 1 digit.

Validation offers modular and composable way of defining and outputting all validation failures which means:

  1. Modular: define validation checks for different fields independently.

  2. Composable: combine smaller validations easily into a validation of a bigger type.

Before implementing Form validation, we need to introduce a type for representing our validation errors. It is a good practice to define all possible errors as a single sum type, so let's go ahead:

Example1 expression
:{data FormValidationError    = EmptyName    | ShortPassword    | NoDigitPassword    deriving stock (Show):}

With Validation we can define checks for individual fields independently and compose them later. First, let's start with defining validation for the name:

Example1 expression
:{validateName :: String -> Validation (NonEmpty FormValidationError) UserNamevalidateName name = UserName name <$ failureIf (null name) EmptyName:}

You can notice a few things about this function:

  1. All errors are collected in NonEmpty, since we want to have guarantees that in case of errors we have at least one failure.

  2. It wraps the result into UserName to tell that validation is passed.

Let's see how this function works:

Example2 expressions
validateName "John"Success "John"validateName ""Failure (EmptyName :| [])

Since Validation provides modular interface for defining checks, we now can define all validation functions for the password separately:

Example1 expression
:{validateShortPassword :: String -> Validation (NonEmpty FormValidationError) PasswordvalidateShortPassword password = Password password <$    failureIf (length password < 8) ShortPassword:}
Example1 expression
:{validatePasswordDigit :: String -> Validation (NonEmpty FormValidationError) PasswordvalidatePasswordDigit password = Password password <$    failureUnless (any isDigit password) NoDigitPassword:}

After we've implemented validations for different Form fields, it's time to combine them together! Validation offers several ways to compose different validations. These ways are provided via different instances of common Haskell typeclasses, specifically:

Semigroup allows combining values inside both Failure and Success but this requires both values to implement the Semigroup instance. This doesn't fit our goal, since Password can't have a reasonble Semigroup instance.

Alternative returns first Success or combines all Failures. We can notice that Alternative also doesn't work for us here.

In our case we are interested in collecting all possible errors and returning Success only when all checks are passed. Fortunately, Applicative is exactly what we need here. So we can use the *> operator to compose all checks for password:

Example1 expression
:{validatePassword :: String -> Validation (NonEmpty FormValidationError) PasswordvalidatePassword password =    validateShortPassword password *> validatePasswordDigit password:}

Let's see how it works:

Example3 expressions
validatePassword "abcd"Failure (ShortPassword :| [NoDigitPassword])validatePassword "abcd1"Failure (ShortPassword :| [])validatePassword "abcd12345"Success "abcd12345"

The validation library provides several convenient combinators, so you can write the password check in a shorter way:

validatePassword :: String -> Validation (NonEmpty FormValidationError) Password
validatePassword = fmap Password . validateAll
    [ (`failureIf`     ShortPassword)   . (< 8) . length
    , (`failureUnless` NoDigitPassword) . any isDigit
    ]

After we've implemented validations for all fields, we can compose them together to produce validation for the whole User. As before, we are going to use the Applicative instance:

Example1 expression
:{validateForm :: Form -> Validation (NonEmpty FormValidationError) UservalidateForm Form{..} = User    <$> validateName formUserName    <*> validatePassword formPassword:}

And it works like a charm:

Example4 expressions
validateForm (Form "" "")Failure (EmptyName :| [ShortPassword,NoDigitPassword])validateForm (Form "John" "abc")Failure (ShortPassword :| [NoDigitPassword])validateForm (Form "Jonh" "qwertypassword")Failure (NoDigitPassword :| [])validateForm (Form "Jonh" "qwertypassword123")Success (User {userName = "Jonh", userPassword = "qwertypassword123"})

Interface functions

8 declarations
valuevalidation :: (e -> x) -> (a -> x) -> Validation e a -> x
#

Transforms the value of the given Validation into x using provided functions that can transform Failure and Success value into the resulting type respectively.

Example3 expressions
let myValidation = validation (<> " world!") (show . (* 10))myValidation (Success 100)"1000"myValidation (Failure "Hello")"Hello world!"
valuefailures :: [Validation e a] -> [e]
#

Filters out all Failure values into the new list of es from the given list of Validations.

Note that the order is preserved.

Example1 expression
failures [Failure "Hello", Success 1, Failure "world", Success 2, Failure "!" ]["Hello","world","!"]
valuesuccesses :: [Validation e a] -> [a]
#

Filters out all Success values into the new list of as from the given list of Validations.

Note that the order is preserved.

Example1 expression
successes [Failure "Hello", Success 1, Failure "world", Success 2, Failure "!" ][1,2]
valuepartitionValidations :: [Validation e a] -> ([e], [a])
#

Redistributes the given list of Validations into two lists of es and es, where the first list contains all values of Failures and the second one — Successes correspondingly.

Note that the order is preserved.

Example1 expression
partitionValidations [Failure "Hello", Success 1, Failure "world", Success 2, Failure "!" ](["Hello","world","!"],[1,2])
valuefromFailure :: e -> Validation e a -> e
#

Returns the contents of a Failure-value or a default value otherwise.

Example2 expressions
fromFailure "default" (Failure "failure")"failure"fromFailure "default" (Success 1)"default"
valuefromSuccess :: a -> Validation e a -> a
#

Returns the contents of a Success-value or a default value otherwise.

Example2 expressions
fromSuccess 42 (Success 1)1fromSuccess 42 (Failure "failure")42

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:

Example1 expression
:{validateNameVerbose :: String -> Validation (NonEmpty String) StringvalidateNameVerbose name    | null name = Failure ("Empty Name" :| [])    | otherwise = Success name:}

one can write simply:

Example1 expression
:{validateNameSimple :: String -> Validation (NonEmpty String) StringvalidateNameSimple name = name <$ failureIf (null name) "Empty Name":}
valuefailureIf :: Bool -> e -> Validation (NonEmpty e) ()
#

Returns a Failure in case of the given predicate is True. Returns Success () otherwise.

Example3 expressions
let shouldFail = (==) "I am a failure"failureIf (shouldFail "I am a failure") "I told you so"Failure ("I told you so" :| [])failureIf (shouldFail "I am NOT a failure") "okay"Success ()
valuefailureUnless :: Bool -> e -> Validation (NonEmpty e) ()
#

Returns a Failure unless the given predicate is True. Returns Success () in case of the predicate is satisfied.

Similar to failureIf with the reversed predicate.

failureUnless p ≡ failureIf (not p)
Example3 expressions
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

Validation is usually compared to the Either data type due to the similarity in structure, nature and use case. Here is a quick table you can relate to, in order to see the main properties and differences between these two data types:

Either

Validation

Error result

Left

Failure

Successful result

Right

Success

Applicative

instance

Stops on the first

Left

Aggregates all

Failure

s

Monad

instance

Lawful instance

Cannot

exist

Comparison in example

For the sake of better illustration of the difference between Either and Validation, let's go through the example of how parsing is done with the usage of these types.

Our goal is to parse two given Strings and return their sum in case if both of them are valid Ints. If any of the inputs is failing to be parsed we should return the ParseError which we are introducing right now:

Example1 expression
:{newtype ParseError = ParseError    { nonParsedString :: String    } deriving stock (Show):}

Let's first implement the parsing of single input in the Either context:

Example1 expression
:{parseEither :: String -> Either ParseError IntparseEither input = case readMaybe @Int input of    Just x  -> Right x    Nothing -> Left $ ParseError input:}

And the final function for Either looks like this:

Example1 expression
:{parseSumEither :: String -> String -> Either ParseError IntparseSumEither str1 str2 = do    let x = parseEither str1    let y = parseEither str2    liftA2 (+) x y:}

Let's now test it in action.

Example4 expressions
parseSumEither "1" "2"Right 3parseSumEither "NaN" "42"Left (ParseError {nonParsedString = "NaN"})parseSumEither "15" "Infinity"Left (ParseError {nonParsedString = "Infinity"})parseSumEither "NaN" "infinity"Left (ParseError {nonParsedString = "NaN"})

Note how in the case of both failed parsing we got only the first NaN.

To finish our comparison, let's implement the same functionality using Validation properties.

Example1 expression
:{parseValidation :: String -> Validation (NonEmpty ParseError) IntparseValidation input = case readMaybe @Int input of    Just x  -> Success x    Nothing -> failure $ ParseError input:}
Example1 expression
:{parseSumValidation :: String -> String -> Validation (NonEmpty ParseError) IntparseSumValidation str1 str2 = do    let x = parseValidation str1    let y = parseValidation str2    liftA2 (+) x y:}

It looks almost completely identical except for the resulting type — Validation (NonEmpty ParseError) Int. But let's see if they behave the same way:

Example4 expressions
parseSumValidation "1" "2"Success 3parseSumValidation "NaN" "42"Failure (ParseError {nonParsedString = "NaN"} :| [])parseSumValidation "15" "infinity"Failure (ParseError {nonParsedString = "infinity"} :| [])parseSumValidation "NaN" "infinity"Failure (ParseError {nonParsedString = "NaN"} :| [ParseError {nonParsedString = "infinity"}])

As expected, with Validation we got all parse Failures we received on the way.

Combinators

We are providing several functions for better integration with the Either related code in this section.

Combinators

1 declaration

When* functions

valuewhenSuccess :: Applicative f => x -> Validation e a -> (a -> f x) -> f x
#

Applies the given action to Validation if it is Success and returns the result. In case of Failure the default value is returned.

Example1 expression
whenSuccess "bar" (Failure "foo") (\a -> "success!" <$ print a)"bar"
Example1 expression
whenSuccess "bar" (Success 42) (\a -> "success!" <$ print a)42"success!"
valuewhenFailure :: Applicative f => x -> Validation e a -> (e -> f x) -> f x
#

Applies the given action to Validation if it is Failure and returns the result. In case of Success the default value is returned.

Example1 expression
whenFailure "bar" (Failure 42) (\a -> "foo" <$ print a)42"foo"
Example1 expression
whenFailure "bar" (Success 42) (\a -> "foo" <$ print a)"bar"
valuewhenSuccessM :: Monad m => x -> m (Validation e a) -> (a -> m x) -> m x
#

Monadic version of whenSuccess. Applies monadic action to the given Validation in case of Success. Returns the resulting value, or provided default.

Example1 expression
whenSuccessM "bar" (pure $ Failure "foo") (\a -> "success!" <$ print a)"bar"
Example1 expression
whenSuccessM "bar" (pure $ Success 42) (\a -> "success!" <$ print a)42"success!"
valuewhenFailureM :: Monad m => x -> m (Validation e a) -> (e -> m x) -> m x
#

Monadic version of whenFailure. Applies monadic action to the given Validation in case of Failure. Returns the resulting value, or provided default.

Example1 expression
whenFailureM "bar" (pure $ Failure 42) (\a -> "foo" <$ print a)42"foo"
Example1 expression
whenFailureM "bar" (pure $ Success 42) (\a -> "foo" <$ print a)"bar"

Maybe conversion