packages feed

validation-micro (empty) → 0.1.0.0

raw patch · 6 files changed

+1031/−0 lines, 6 filesdep +basedep +deepseqsetup-changed

Dependencies added: base, deepseq

Files

+ CHANGELOG.md view
@@ -0,0 +1,13 @@+# Changelog for `validation-micro`++All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),+and this project adheres to the+[Haskell Package Versioning Policy](https://pvp.haskell.org/).++## Unreleased++## 0.1.0.0++First version
+ LICENSE view
@@ -0,0 +1,28 @@+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,38 @@+# validation-micro++[![GitHub CI](https://github.com/unfoldml/validation-micro/workflows/CI/badge.svg)](https://github.com/unfoldml/validation-micro/actions)++Lightweight pure data validation based on `Applicative` .++`validation-micro` is built around the following data type:++```haskell+data Validation e a+    = Failure e+    | Success a+```++This data type is similar to `Either` but allows accumulating all+errors instead of short-circuiting on the first one.++## Comparison with other packages++`validation-micro` is not the only package that provides the `Validation` data type. +Here are some differences and commonalities to other validation packages:+++ **Lightweight**. `validation-micro` depends only on `base` and+  `deepseq`, which make it fast to build. +  So adding validation capabilities to your+  library or application doesn't contribute much to your dependency+  footprint.++ **Future-proof**. Both `base` and `deepseq` are GHC boot packages, which means that `validation-micro` can be imported as long as GHC can run.++ **No Selective instance**. This means you cannot choose which validation to use based on the value. On the other hand, we don't need to depend on `selective` which is a somewhat experimental package.++ **No Monad instance** - but there is a `bindValidation` utility function which behaves like `(>>=)`.++## Copyright++Copyright:  (c) 2014 Chris Allen, Edward Kmett+            (c) 2018-2023 Kowainik+            (c) 2023 Marco Zocca, UnfoldML+License: BSD3+Maintainer:  oss@unfoldml.com
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Validation/Micro.hs view
@@ -0,0 +1,884 @@+{-# LANGUAGE DeriveAnyClass       #-}+{-# LANGUAGE DeriveDataTypeable   #-}+{-# language DerivingStrategies #-}+{-# language DeriveGeneric #-}+{-# language LambdaCase #-}++{- |+Copyright:  (c) 2014 Chris Allen+            (c) 2014 Edward Kmett+            (c) 2018-2023 Kowainik+            (c) 2023 Marco Zocca, UnfoldML+SPDX-License-Identifier: BSD-3-Clause+Maintainer:  oss@unfoldml.com+Stability:   Stable+Portability: Portable++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:++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+'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:++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'.+-}++module Validation.Micro (+  -- * Type+         Validation (..)++         -- * How to use+         -- $use++         -- * Interface functions+       , isFailure+       , isSuccess+       , validation+       , failures+       , successes+       , partitionValidations+       , fromFailure+       , fromSuccess+       , bindValidation++         -- ** 'NonEmpty' combinators+         -- $nonEmptyCombinators+       , failure+       , failureIf+       , failureUnless++         -- ** 'Either' conversion+         -- $either+       , validationToEither+       , eitherToValidation++         -- * Combinators+       , validateAll++      -- * When* functions+    , whenSuccess+    , whenFailure+    , whenSuccess_+    , whenFailure_+    , whenSuccessM+    , whenFailureM+    , whenSuccessM_+    , whenFailureM_++      -- * 'Maybe' conversion+    , failureToMaybe+    , successToMaybe+    , maybeToFailure+    , maybeToSuccess+  ) where++import Control.Applicative (Alternative (..), Applicative (..))+import Data.Bifoldable (Bifoldable (..))+import Data.Bifunctor (Bifunctor (..))+import Data.Bitraversable (Bitraversable (..))+import Data.Data (Data)+import Control.DeepSeq (NFData, NFData1, NFData2(..))+import Data.Foldable (Foldable (..))+import Data.List.NonEmpty (NonEmpty (..))++import GHC.Generics (Generic(..), Generic1(..))++{- | '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 @'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.+-}+data Validation e a+    = Failure e+    -- ^ Validation failure. The @e@ type is supposed to implement the 'Semigroup' instance.+    | Success a+    -- ^ Successful validation result of type @a@.+    deriving stock (Eq, Ord, Show, Generic, Generic1, Data)+    deriving anyclass (NFData, NFData1)++{- | Allows changing the value inside 'Success' with a given function.++__Examples__++>>> fmap (+1) (Success 9)+Success 10+>>> fmap (+1) (Failure ["wrong"])+Failure ["wrong"]+-}+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 (<$) #-}+++{- | 'Semigroup' allows merging multiple 'Validation's into single one+by combining values inside both 'Failure' and 'Success'. The '<>'+operator merges two 'Validation's following the below rules:++1. If both values are 'Failure's, returns a new 'Failure' with+accumulated errors.+2. If both values are 'Success'ful, 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__++>>> success1 = Success [9] :: Validation [String] [Int]+>>> success2 = Success [15] :: Validation [String] [Int]+>>> failure1 = Failure ["WRONG"] :: Validation [String] [Int]+>>> failure2 = Failure ["FAIL"]  :: Validation [String] [Int]++>>> success1 <> success2+Success [9,15]+>>> failure1 <> failure2+Failure ["WRONG","FAIL"]+>>> success1 <> failure1+Failure ["WRONG"]+>>> failure2 <> success1 <> success2 <> failure1+Failure ["FAIL","WRONG"]+-}+instance (Semigroup e, Semigroup a) => Semigroup (Validation e a) where+    -- (<>) :: Validation e a -> Validation e a -> Validation e a+    (<>) = liftA2 (<>)+    {-# INLINE (<>) #-}++{- | @'mempty' :: 'Validation' e a@ is @Success@ which stores+@'mempty' :: a@ to be consistent with the 'Semigroup' instance.++__Examples__++>>> mempty :: Validation String [Bool]+Success []+-}+instance (Semigroup e, Monoid a) => Monoid (Validation e a) where+    mempty = Success mempty+    {-# INLINE mempty #-}++    mappend = (<>)+    {-# INLINE mappend #-}++++{- | 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__++>>> success1 = Success 9 :: Validation [String] Int+>>> success2 = Success 15 :: Validation [String] Int+>>> successF = Success (* 2) :: Validation [String] (Int -> Int)+>>> failure1 = Failure ["WRONG"] :: Validation [String] Int+>>> failure2 = Failure ["FAIL"]  :: Validation [String] Int++>>> successF <*> success1+Success 18+>>> successF <*> failure1+Failure ["WRONG"]+>>> (+) <$> success1 <*> success2+Success 24+>>> (+) <$> failure1 <*> failure2+Failure ["WRONG","FAIL"]+>>> liftA2 (+) success1 failure1+Failure ["WRONG"]+>>> liftA3 (,,) failure1 success1 failure2+Failure ["WRONG","FAIL"]++Implementations of all functions are lazy and they correctly work if some+arguments are not fully evaluated.++>>> failure1 *> failure2+Failure ["WRONG","FAIL"]+>>> isFailure $ failure1 *> failure2+True+>>> epicFail = error "Impossible validation" :: Validation [String] Int+>>> isFailure $ failure1 *> epicFail+True+-}+instance Semigroup e => Applicative (Validation e) where+    -- pure :: a -> Validation e a+    pure = Success+    {-# INLINE pure #-}++    Failure e1 <*> b = Failure $ case b of+        Failure e2 -> e1 <> e2+        Success _  -> e1+    Success _ <*> Failure e = Failure e+    Success f <*> Success a = Success (f a)+    {-# INLINE (<*>) #-}++    Failure e1 *> b = Failure $ case b of+        Failure e2 -> e1 <> e2+        Success _  -> e1+    Success _ *> Failure e = Failure e+    Success _ *> Success b = Success b+    {-# INLINE (*>) #-}++    Failure e1 <* b = Failure $ case b of+        Failure e2 -> e1 <> e2+        Success _  -> e1+    Success _ <* Failure e = Failure e+    Success a <* Success _ = Success a+    {-# INLINE (<*) #-}++    liftA2 _ (Failure e1) b = Failure $ case b of+        Failure e2 -> e1 <> e2+        Success _  -> e1+    liftA2 _ (Success _) (Failure e) = Failure e+    liftA2 f (Success a) (Success b) = Success (f a b)+    {-# INLINE liftA2 #-}++-- | NB Validation is not a monad though+bindValidation :: Validation e a -> (a -> Validation e b) -> Validation e b+bindValidation v f = case v of+  Failure e -> Failure e+  Success a -> f a+++{- | This instance implements the behaviour when the first 'Success'+is returned. Otherwise all 'Failure's are combined.++__Examples__++>>> success1 = Success [9] :: Validation [String] [Int]+>>> success2 = Success [15] :: Validation [String] [Int]+>>> failure1 = Failure ["WRONG"] :: Validation [String] [Int]+>>> failure2 = Failure ["FAIL"]  :: Validation [String] [Int]++>>> success1 <|> success2+Success [9]+>>> failure1 <|> failure2+Failure ["WRONG","FAIL"]+>>> failure2 <|> success2+Success [15]+-}+instance (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 (<|>) #-}++++{- | 'Foldable' for 'Validation' allows folding values inside 'Success'.++__Examples__++>>> fold (Success [16])+[16]+>>> fold (Failure "WRONG!" :: Validation String [Int])+[]+-}+instance Foldable (Validation e) where+    fold = \case+        Failure _ -> mempty+        Success a -> a+    {-# INLINE fold #-}++    foldMap f = \case+        Failure _ -> mempty+        Success a -> f a+    {-# INLINE foldMap #-}++    foldr f x = \case+        Failure _ -> x+        Success a -> f a x+    {-# INLINE foldr #-}++    foldr' = foldr+    {-# INLINE foldr' #-}++    foldl f x = \case+        Failure _ -> x+        Success a -> f x a+    {-# INLINE foldl #-}++    foldl' = foldl+    {-# INLINE foldl' #-}++    toList = \case+        Failure _ -> []+        Success a -> [a]+    {-# INLINE toList #-}++    null = \case+        Failure _ -> True+        Success _ -> False+    {-# INLINE null #-}++    length = \case+        Failure _ -> 0+        Success _ -> 1+    {-# INLINE length #-}++    elem x = \case+        Failure _ -> False+        Success a -> x == a+    {-# INLINE elem #-}++    sum = \case+        Failure _ -> 0+        Success a -> a+    {-# INLINE sum #-}++    product = \case+        Failure _ -> 1+        Success a -> a+    {-# INLINE product #-}++++{- | Traverse values inside 'Success' 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"])+-}+instance Traversable (Validation e) where+    traverse f (Success a) = Success <$> f a+    traverse _ (Failure e) = pure (Failure e)+    {-# INLINE traverse #-}++    sequenceA = \case+        Failure e -> pure (Failure e)+        Success f -> Success <$> f+    {-# INLINE sequenceA #-}++{- | Similar to 'Functor' but allows mapping of values inside both+'Failure' and 'Success'.++__Examples__++>>> bimap length show (Success 50)+Success "50"+>>> bimap length show (Failure ["15", "9"])+Failure 2+-}+instance Bifunctor Validation where+    bimap f _ (Failure e) = Failure (f e)+    bimap _ g (Success a) = Success (g a)+    {-# INLINE bimap #-}++    first f (Failure e) = Failure (f e)+    first _ (Success a) = Success a+    {-# INLINE first #-}++    second _ (Failure e) = Failure e+    second g (Success a) = Success (g a)+    {-# INLINE second #-}+++++{- | Similar to 'Foldable' but allows folding both 'Failure' and+'Success' to the same monoidal value according to given functions.++__Examples__++>>> one x = [x]+>>> bifoldMap id (one . show) (Success 15)+["15"]+>>> bifoldMap id (one . show) (Failure ["Wrong", "Fail"])+["Wrong","Fail"]+-}+instance Bifoldable Validation where+    bifoldMap f _ (Failure e) = f e+    bifoldMap _ g (Success a) = g a+    {-# INLINE bifoldMap #-}++{- | Similar to 'Traversable' but traverses both 'Failure' and+'Success' 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 [])+Nothing+-}+instance Bitraversable Validation where+    bitraverse f _ (Failure e) = Failure <$> f e+    bitraverse _ g (Success a) = Success <$> g a+    {-# INLINE bitraverse #-}++instance NFData2 Validation where+    liftRnf2 f _s (Failure x) = f x+    liftRnf2 _f s (Success y) = s y+++++{- |+== 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"+-}+validationToEither :: Validation e a -> Either e a+validationToEither = \case+    Failure e -> Left e+    Success a -> Right a+{-# INLINE validationToEither #-}++{- | Transform an 'Either' into a 'Validation'.++>>> eitherToValidation (Right "whoop")+Success "whoop"++>>> eitherToValidation (Left "nahh")+Failure "nahh"+-}+eitherToValidation :: Either e a -> Validation e a+eitherToValidation = \case+    Left e  -> Failure e+    Right a -> Success a+{-# INLINE eitherToValidation #-}++++----------------------------------------------------------------------------+-- Interface+----------------------------------------------------------------------------++{- | Predicate on if the given 'Validation' is 'Failure'.++>>> isFailure (Failure 'e')+True+>>> isFailure (Success 'a')+False+-}+isFailure :: Validation e a -> Bool+isFailure = \case+    Failure _ -> True+    Success _ -> False++{- | Predicate on if the given 'Validation' is 'Success'.++>>> isSuccess (Success 'a')+True+>>> isSuccess (Failure 'e')+False+-}+isSuccess :: Validation e a -> Bool+isSuccess = \case+    Success _ -> True+    Failure _ -> False++{- | 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!"+-}+validation :: (e -> x) -> (a -> x) -> Validation e a -> x+validation fe fa = \case+    Success a -> fa a+    Failure e -> fe e++{- | 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","!"]+-}+failures :: [Validation e a] -> [e]+failures v = [e | Failure e <- v]+{-# INLINE failures #-}++{- | 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]+-}+successes :: [Validation e a] -> [a]+successes v = [a | Success a <- v]+{-# INLINE successes #-}++{- | 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])+-}+partitionValidations :: [Validation e a] -> ([e], [a])+partitionValidations = go+  where+    go :: [Validation e a] -> ([e], [a])+    go []               = ([], [])+    go (Failure e:rest) = first  (e:) $ go rest+    go (Success a:rest) = second (a:) $ go rest++{- | Returns the contents of a 'Failure'-value or a default value otherwise.++>>> fromFailure "default" (Failure "failure")+"failure"+>>> fromFailure "default" (Success 1)+"default"+-}+fromFailure :: e -> Validation e a -> e+fromFailure _ (Failure e) = e+fromFailure e _           = e++{- | Returns the contents of a 'Success'-value or a default value otherwise.++>>> fromSuccess 42 (Success 1)+1+>>> fromSuccess 42 (Failure "failure")+42+-}+fromSuccess :: a -> Validation e a -> a+fromSuccess _ (Success a) = a+fromSuccess a _           = a++----------------------------------------------------------------------------+-- NonEmpty Combinators+----------------------------------------------------------------------------++{- $nonEmptyCombinators++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"+:}++-}++{- | Create a 'Failure' of 'NonEmpty' list with a single given error.++>>> failure "I am a failure"+Failure ("I am a failure" :| [])+-}+failure :: e -> Validation (NonEmpty e) a+failure e = Failure (e :| [])+{-# INLINE failure #-}++{- | Returns a 'Failure' in case of the given predicate is 'True'.+Returns @'Success' ()@ otherwise.++>>> 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 ()+-}+failureIf :: Bool -> e -> Validation (NonEmpty e) ()+failureIf p e+    | p = failure e+    | otherwise = Success ()+{-# INLINE failureIf #-}++{- | 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)+@++>>> 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" :| [])+-}+failureUnless :: Bool -> e -> Validation (NonEmpty e) ()+failureUnless p e+    | p = Success ()+    | otherwise = failure e+{-# INLINE failureUnless #-}++++{- | 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])+@+-}+validateAll+    :: (Foldable f, Semigroup e)+    => f (a -> Validation e b)+    -> a+    -> Validation e a+validateAll fs a = foldl' (\res f -> res <* f a) (Success a) fs+{-# INLINE validateAll #-}++{- | 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"+-}+whenFailure :: Applicative f => x -> Validation e a -> (e -> f x) -> f x+whenFailure _ (Failure e) f = f e+whenFailure a (Success _) _ = pure a+{-# INLINE whenFailure #-}++{- | 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+-}+whenFailure_ :: Applicative f => Validation e a -> (e -> f ()) -> f ()+whenFailure_ = whenFailure ()+{-# INLINE whenFailure_ #-}++{- | 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"+-}+whenFailureM :: Monad m => x -> m (Validation e a) -> (e -> m x) -> m x+whenFailureM x mv f = mv >>= \v -> whenFailure x v f+{-# INLINE whenFailureM #-}++{- | 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+-}+whenFailureM_ :: Monad m => m (Validation e a) -> (e -> m ()) -> m ()+whenFailureM_ mv f = mv >>= \v -> whenFailure_ v f+{-# INLINE whenFailureM_ #-}++{- | 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!"+-}+whenSuccess :: Applicative f => x -> Validation e a -> (a -> f x) -> f x+whenSuccess x (Failure  _) _ = pure x+whenSuccess _ (Success a) f  = f a+{-# INLINE whenSuccess #-}++{- | 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+-}+whenSuccess_ :: Applicative f => Validation e a -> (a -> f ()) -> f ()+whenSuccess_ = whenSuccess ()+{-# INLINE whenSuccess_ #-}++{- | 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!"+-}+whenSuccessM :: Monad m => x -> m (Validation e a) -> (a -> m x) -> m x+whenSuccessM x mv f = mv >>= \v -> whenSuccess x v f+{-# INLINE whenSuccessM #-}++{- | 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+-}+whenSuccessM_ :: Monad m => m (Validation e a) -> (a -> m ()) -> m ()+whenSuccessM_ mv f = mv >>= \v -> whenSuccess_ v f+{-# INLINE whenSuccessM_ #-}+++{- | Maps 'Failure' of 'Validation' to 'Just'.++>>> failureToMaybe (Failure True)+Just True+>>> failureToMaybe (Success "aba")+Nothing+-}+failureToMaybe :: Validation e a -> Maybe e+failureToMaybe = validation Just (const Nothing)+{-# INLINE failureToMaybe #-}++{- | Maps 'Success' of 'Validation' to 'Just'.++>>> successToMaybe (Failure True)+Nothing+>>> successToMaybe (Success "aba")+Just "aba"+-}+successToMaybe :: Validation e a -> Maybe a+successToMaybe = validation (const Nothing) Just+{-# INLINE successToMaybe #-}++{- | Maps 'Just' to 'Failure' In case of 'Nothing' it wraps the given default+value into 'Success'.++>>> maybeToFailure True (Just "aba")+Failure "aba"+>>> maybeToFailure True Nothing+Success True+-}+maybeToFailure :: a -> Maybe e -> Validation e a+maybeToFailure a = maybe (Success a) Failure+{-# INLINE maybeToFailure #-}++{- | Maps 'Just' to 'Success'. In case of 'Nothing' it wraps the given default+value into 'Failure'++>>> maybeToSuccess True (Just "aba")+Success "aba"+>>> maybeToSuccess True Nothing+Failure True+-}+maybeToSuccess :: e -> Maybe a -> Validation e a+maybeToSuccess e = maybe (Failure e) Success+{-# INLINE maybeToSuccess #-}
+ validation-micro.cabal view
@@ -0,0 +1,66 @@+name:                validation-micro+version:             0.1.0.0+synopsis:            Lighweight pure data validation based on Applicative+description:+    Lighweight pure data validation based on Applicative. The library provides the+    following Either-like data type with suitable instances like Semigroup to accumulate validation errors in the `Failure` branch :+    .+    @+    __data__ Validation e a+    \    = Failure e+    \    | Success a+    @+    .++homepage:            https://github.com/unfoldml/validation-micro+license:             BSD3+license-file:        LICENSE+author:              Marco Zocca+maintainer:          oss@unfoldml.com+copyright:           2023 Marco Zocca+category:            Validation+build-type:          Simple+extra-source-files:  README.md+                     CHANGELOG.md+                     LICENSE+cabal-version:       >=1.10+tested-with:         GHC == 9.2.8++library+  default-language:    Haskell2010+  hs-source-dirs:      src+  exposed-modules:     Validation.Micro+  build-depends:       base >= 4.7 && < 5+                     , deepseq+  ghc-options:         -Wall+                       -Wcompat+                       -Widentities+                       -Wincomplete-record-updates+                       -Wincomplete-uni-patterns+                       -Wmissing-export-lists+                       -Wpartial-fields+                       -Wredundant-constraints++-- test-suite spec+--   default-language:    Haskell2010+--   type:                exitcode-stdio-1.0+--   hs-source-dirs:      test+--   main-is:             Spec.hs+--   other-modules:       LibSpec+--   build-depends:       base+--                      , validation-micro+--                      , hspec+--                      , QuickCheck+--   ghc-options:         -Wall+--                        -Wcompat+--                        -Widentities+--                        -Wincomplete-record-updates+--                        -Wincomplete-uni-patterns+--                        -Wmissing-export-lists+--                        -Wmissing-home-modules+--                        -Wpartial-fields+--                        -Wredundant-constraints++source-repository head+  type:     git+  location: https://github.com/unfoldml/validation-micro