#%      !"#$E(c) 2014 Chris Allen, Edward Kmett (c) 2018-2020 KowainikMPL-2.0Kowainik <xrom.xkov@gmail.com>None"#%,./1279;HMPUVX_kQ%validation-selective,Helper type family to produce error messagesvalidation-selectived is a polymorphic sum type for storing either all validation failures or validation success. Unlike &&, which returns only the first error, " accumulates all errors using the ' typeclass.Usually type variables in  e a are used as follows:eI: 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:  [(] UserEither list of (3 error messages or a validated value of a custom User type.  () UserValidationError) UserSimilar 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.validation-selectiveValidation failure. The e# type is supposed to implement the ' instance.validation-selective%Successful validation result of type a.validation-selective Transform a  into an &.$validationToEither (Success "whoop") Right "whoop"#validationToEither (Failure "nahh") Left "nahh"validation-selective Transform an & into a ."eitherToValidation (Right "whoop")Success "whoop" eitherToValidation (Left "nahh")Failure "nahh"validation-selectivePredicate on if the given  is .isFailure (Failure 'e')TrueisFailure (Success 'a')Falsevalidation-selectivePredicate on if the given  is .isSuccess (Success 'a')TrueisSuccess (Failure 'e')Falsevalidation-selective"Transforms the value of the given  into x- using provided functions that can transform  and , value into the resulting type respectively.<let myValidation = validation (<> " world!") (show . (* 10))myValidation (Success 100)"1000"myValidation (Failure "Hello")"Hello world!"validation-selectiveFilters out all  values into the new list of es from the given list of s.!Note that the order is preserved.Ofailures [Failure "Hello", Success 1, Failure "world", Success 2, Failure "!" ]["Hello","world","!"] validation-selectiveFilters out all  values into the new list of as from the given list of s.!Note that the order is preserved.Psuccesses [Failure "Hello", Success 1, Failure "world", Success 2, Failure "!" ][1,2] validation-selective Redistributes the given list of s into two lists of es and e/s, where the first list contains all values of s and the second one  es correspondingly.!Note that the order is preserved.[partitionValidations [Failure "Hello", Success 1, Failure "world", Success 2, Failure "!" ](["Hello","world","!"],[1,2]) validation-selectiveReturns the contents of a $-value or a default value otherwise.)fromFailure "default" (Failure "failure") "failure"!fromFailure "default" (Success 1) "default" validation-selectiveReturns the contents of a $-value or a default value otherwise.fromSuccess 42 (Success 1)1"fromSuccess 42 (Failure "failure")42 validation-selective Create a  of ) list with a single given error.failure "I am a failure" Failure ("I am a failure" :| [])validation-selective Returns a # in case of the given predicate is * . Returns  () otherwise.&let shouldFail = (==) "I am a failure"7failureIf (shouldFail "I am a failure") "I told you so"Failure ("I told you so" :| [])2failureIf (shouldFail "I am NOT a failure") "okay" Success ()validation-selective Returns a  unless the given predicate is * . Returns  ()' in case of the predicate is satisfied. Similar to  with the reversed predicate.  p "a  (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" :| [])validation-selective Similar to + but traverses both  and # with given effectful computations.Examples+parseInt = readMaybe :: String -> Maybe Int.bitraverse listToMaybe parseInt (Success "42")Just (Success 42)/bitraverse listToMaybe parseInt (Success "int")Nothing.bitraverse listToMaybe parseInt (Failure [15])Just (Failure 15),bitraverse listToMaybe parseInt (Failure [])Nothingvalidation-selective Similar to , but allows folding both  and 9 to the same monoidal value according to given functions.Examples one x = [x]&bifoldMap id (one . show) (Success 15)["15"]5bifoldMap id (one . show) (Failure ["Wrong", "Fail"])["Wrong","Fail"]validation-selective Similar to -* but allows mapping of values inside both  and .Examplesbimap length show (Success 50) Success "50"'bimap length show (Failure ["15", "9"]) Failure 2validation-selectiveTraverse values inside ! with some effectful computation.Examples+parseInt = readMaybe :: String -> Maybe Int traverse parseInt (Success "42")Just (Success 42)!traverse parseInt (Success "int")Nothing"traverse parseInt (Failure ["42"])Just (Failure ["42"])validation-selective, for  allows folding values inside .Examplesfold (Success [16])[16]2fold (Failure "WRONG!" :: Validation String [Int])[]validation-selective6This instance implements the behaviour when the first  is returned. Otherwise all s are combined.Examples3success1 = Success [9] :: Validation [String] [Int]4success2 = Success [15] :: Validation [String] [Int]9failure1 = Failure ["WRONG"] :: Validation [String] [Int]9failure2 = Failure ["FAIL"] :: Validation [String] [Int]success1 <|> success2 Success [9]failure1 <|> failure2Failure ["WRONG","FAIL"]failure2 <|> success2 Success [15]validation-selective. functors from the  -https://hackage.haskell.org/package/selective selectiveZ package. This instance allows choosing which validations to apply based on value inside.  can't have a lawful / instance but it's highly desirable to have the monadic behavior in cases when you want future checks depend on previous values. .H allows to circumvent this limitation by providing the desired behavior.ExamplesTo understand better, how .R can be helpful, let's consider a typical usage example with validating passwords.:{newtype Password = Password { unPassword :: String } deriving stock (Show):}TWhen user enters a password in some form, we want to check the following conditions: Password must not be empty.,Password must contain at least 8 characters.'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.:{data PasswordValidationError = EmptyPassword | ShortPassword | NoDigitPassword deriving stock (Show):}OAnd, again, we can implement independent functions to validate all these cases:Ptype PasswordValidation = Validation (NonEmpty PasswordValidationError) Password:{5validateEmptyPassword :: String -> PasswordValidation5validateEmptyPassword password = Password password <$+ failureIf (null password) EmptyPassword:}:{5validateShortPassword :: String -> PasswordValidation5validateShortPassword password = Password password <$1 failureIf (length password < 8) ShortPassword:}:{5validatePasswordDigit :: String -> PasswordValidation5validatePasswordDigit password = Password password <$8 failureUnless (any isDigit password) NoDigitPassword:}FAnd we can easily compose all these checks into single validation for Password using 0 instance::{0validatePassword :: String -> PasswordValidationvalidatePassword password =" validateEmptyPassword password% *> validateShortPassword password% *> validatePasswordDigit password:}LHowever, if we try using this function, we can notice a problem immediately:validatePassword "":Failure (EmptyPassword :| [ShortPassword,NoDigitPassword])Due to the nature of the 0 instance for , 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 w, 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  had the /& instance. But it can't have a lawful / instance. Fortunately, the . instance for r can help with our problem. But to solve it, we need to write our password validation in a slightly different way.MFirst, we need to write a function that checks whether the password is empty::{1checkEmptyPassword :: String -> Validation e Bool#checkEmptyPassword = Success . null:}Now we can use the ifS function from the  selective$ package to branch on the result of checkEmptyPassword::{0validatePassword :: String -> PasswordValidationvalidatePassword password = ifS! (checkEmptyPassword password) (failure EmptyPassword)F (validateShortPassword password *> validatePasswordDigit password):}:With this implementation we achieved our desired behavior:validatePassword ""Failure (EmptyPassword :| [])validatePassword "abc",Failure (ShortPassword :| [NoDigitPassword])validatePassword "abc123"Failure (ShortPassword :| [])validatePassword "security567"/Success (Password {unPassword = "security567"})validation-selective5This instance if the most important instance for the  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/success1 = Success 9 :: Validation [String] Int0success2 = Success 15 :: Validation [String] Int<successF = Success (* 2) :: Validation [String] (Int -> Int)7failure1 = Failure ["WRONG"] :: Validation [String] Int7failure2 = Failure ["FAIL"] :: Validation [String] IntsuccessF <*> success1 Success 18successF <*> failure1Failure ["WRONG"](+) <$> success1 <*> success2 Success 24(+) <$> failure1 <*> failure2Failure ["WRONG","FAIL"]liftA2 (+) success1 failure1Failure ["WRONG"]&liftA3 (,,) failure1 success1 failure2Failure ["WRONG","FAIL"]lImplementations of all functions are lazy and they correctly work if some arguments are not fully evaluated.failure1 *> failure2Failure ["WRONG","FAIL"] isFailure $ failure1 *> failure2TrueCepicFail = error "Impossible validation" :: Validation [String] Int isFailure $ failure1 *> epicFailTruevalidation-selective1 ::  e a is Success which stores 1 :: a to be consistent with the ' instance.Examples"mempty :: Validation String [Bool] Success []validation-selective' allows merging multiple 2s into single one by combining values inside both  and . The 2 operator merges two s following the below rules: If both values are s, returns a new  with accumulated errors.If both values are ful, returns a new  with combined success using ' for values inside .If one value is  and another one is , then  is returned.Examples3success1 = Success [9] :: Validation [String] [Int]4success2 = Success [15] :: Validation [String] [Int]9failure1 = Failure ["WRONG"] :: Validation [String] [Int]9failure2 = Failure ["FAIL"] :: Validation [String] [Int]success1 <> success2Success [9,15]failure1 <> failure2Failure ["WRONG","FAIL"]success1 <> failure1Failure ["WRONG"],failure2 <> success1 <> success2 <> failure1Failure ["FAIL","WRONG"]validation-selective!Allows changing the value inside  with a given function.Examplesfmap (+1) (Success 9) Success 10fmap (+1) (Failure ["wrong"])Failure ["wrong"]validation-selective&CAUTION2& This instance is for custom error display only.&It's not possible to implement lawful / instance for .?In case it is used by mistake, the user will see the following:GSuccess 42 >>= \n -> if even n then Success n else Failure ["Not even"]...:... Type 'Validation' doesn't have lawful 'Monad' instanceG which means that you can't use 'Monad' methods with 'Validation'....  3      !"#$%&'()'*+'*,'*-./0'12'34'*5678'*9'*:'*;'*<=$validation-selective-0.0.0.0-inplace ValidationFailureSuccessvalidationToEithereitherToValidation isFailure isSuccess validationfailures successespartitionValidations fromFailure fromSuccessfailure failureIf failureUnless$fNFData2Validation$fBitraversableValidation$fBifoldableValidation$fBifunctorValidation$fTraversableValidation$fFoldableValidation$fAlternativeValidation$fSelectiveValidation$fApplicativeValidation$fMonoidValidation$fSemigroupValidation$fFunctorValidation$fMonadValidation$fEqValidation$fOrdValidation$fShowValidation$fGenericValidation$fGeneric1Validation$fDataValidation$fNFDataValidation$fNFData1ValidationNoValidationMonadErrorbase Data.EitherEitherGHC.Base SemigroupStringNonEmptyghc-prim GHC.TypesTrueData.Traversable Traversable Data.FoldableFoldableFunctorNselective-0.4-1ee6202d2523ff1dfdd7c83ff987a578853a3b5cf275f1a10832bd00237fd529Control.Selective SelectiveMonad Applicativemempty<>