contracheck-applicative-0.2.0: Validation types/typeclass based on the contravariance.
Copyright(c) Fabian Birkmann 2020
LicenseGPL-3
Maintainer99fabianb@sis.gl
Stabilityexperimental
PortabilityPOSIX
Safe HaskellNone
LanguageHaskell2010

Control.Validation.Check

Description

Types and functions to check properties of your data. To make best use of these functions you should check out Data.Functor.Contravariant. For an introduction see the README.

Synopsis

Unvalidated values

A newtype around unvalidated values so one cannot use the value until it is validated. You can create an Unvalidated via unvalidated

WARNING The Unvalidated data construcotr should NOT be used in real code and is exported solely to be used in -XDeriving-clauses

, but it is often more convient to write an orphan instance: If for example you have a JSON api and want to validate incoming data, you can write (using -XStandaloneDeriving, -XDerivingStrategies, -XDerivingVia):

import Data.Aeson(FromJSON)
deriving via (a :: Type) instance (FromJSON a) => FromJSON (Unvalidated a)

newtype Unvalidated (a :: Type) Source #

Constructors

MkUnvalidated

Warning: Use unvalidated. The data constructor Unvalidated is not to be used in code and is only exported for use in deriving clauses

Fields

Instances

Instances details
Monad Unvalidated Source # 
Instance details

Defined in Control.Validation.Check

Methods

(>>=) :: Unvalidated a -> (a -> Unvalidated b) -> Unvalidated b #

(>>) :: Unvalidated a -> Unvalidated b -> Unvalidated b #

return :: a -> Unvalidated a #

Functor Unvalidated Source # 
Instance details

Defined in Control.Validation.Check

Methods

fmap :: (a -> b) -> Unvalidated a -> Unvalidated b #

(<$) :: a -> Unvalidated b -> Unvalidated a #

Applicative Unvalidated Source # 
Instance details

Defined in Control.Validation.Check

Methods

pure :: a -> Unvalidated a #

(<*>) :: Unvalidated (a -> b) -> Unvalidated a -> Unvalidated b #

liftA2 :: (a -> b -> c) -> Unvalidated a -> Unvalidated b -> Unvalidated c #

(*>) :: Unvalidated a -> Unvalidated b -> Unvalidated b #

(<*) :: Unvalidated a -> Unvalidated b -> Unvalidated a #

Eq a => Eq (Unvalidated a) Source # 
Instance details

Defined in Control.Validation.Check

Ord a => Ord (Unvalidated a) Source # 
Instance details

Defined in Control.Validation.Check

Show a => Show (Unvalidated a) Source # 
Instance details

Defined in Control.Validation.Check

Generic (Unvalidated a) Source # 
Instance details

Defined in Control.Validation.Check

Associated Types

type Rep (Unvalidated a) :: Type -> Type #

Methods

from :: Unvalidated a -> Rep (Unvalidated a) x #

to :: Rep (Unvalidated a) x -> Unvalidated a #

type Rep (Unvalidated a) Source # 
Instance details

Defined in Control.Validation.Check

type Rep (Unvalidated a) = D1 ('MetaData "Unvalidated" "Control.Validation.Check" "contracheck-applicative-0.2.0-inplace" 'True) (C1 ('MetaCons "MkUnvalidated" 'PrefixI 'True) (S1 ('MetaSel ('Just "unsafeValidate") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 a)))

Types for checks

Check results

The result of (possibly many) checks. It is either valid or a sequence of all the errors that occurred during the check. The semigroup operation is eager to collect all possible erros.

data CheckResult (e :: Type) Source #

Constructors

Passed 
Failed !(Seq e) 

Instances

Instances details
Functor CheckResult Source # 
Instance details

Defined in Control.Validation.Check

Methods

fmap :: (a -> b) -> CheckResult a -> CheckResult b #

(<$) :: a -> CheckResult b -> CheckResult a #

Eq e => Eq (CheckResult e) Source # 
Instance details

Defined in Control.Validation.Check

Show e => Show (CheckResult e) Source # 
Instance details

Defined in Control.Validation.Check

Generic (CheckResult e) Source # 
Instance details

Defined in Control.Validation.Check

Associated Types

type Rep (CheckResult e) :: Type -> Type #

Methods

from :: CheckResult e -> Rep (CheckResult e) x #

to :: Rep (CheckResult e) x -> CheckResult e #

Semigroup (CheckResult e) Source # 
Instance details

Defined in Control.Validation.Check

Monoid (CheckResult e) Source # 
Instance details

Defined in Control.Validation.Check

type Rep (CheckResult e) Source # 
Instance details

Defined in Control.Validation.Check

type Rep (CheckResult e) = D1 ('MetaData "CheckResult" "Control.Validation.Check" "contracheck-applicative-0.2.0-inplace" 'False) (C1 ('MetaCons "Passed" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "Failed" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Seq e))))

checkResult :: a -> (Seq e -> a) -> CheckResult e -> a Source #

A fold for CheckResult

failsNoMsg :: CheckResult e Source #

Throwing an error without a message.

checkResultToEither Source #

Arguments

:: a

default value

-> CheckResult e 
-> Either (Seq e) a 

The Check type

The type of a (lifted) check. A Check takes an unvalidated data and produces a CheckResult. It may need an additional context m. If the context is trivial ('m ≡ Identity') helper types/functions are postfixed by an apostrophe `'`. A Check is not a validation function, as it does not produce any values (to validated data using a Check use validateBy). The reason for this is that it gives Check some useful instances, as it now is contravariant in a and not invariant in a like e.g. `a -> Either b a`

  • Contravariant
newtype Even = Even { getEven :: Int }
checkEven :: Check' Text Even
checkEven = (== 0) . (`mod` 2) . getEven ?> mappend "Number is not even: " . show

newtype Odd = Odd { getOdd :: Int }
checkOdd :: Check' Text Odd
checkOdd = Even . (+1) . getOdd >$< checkEven
  • Semigroup/Monoid: Allows for easy composition of checks
newtype EvenAndOdd = EvenAndOdd { getEvenAndOdd :: Int }
checkevenAndOdd :: Check' Text EvenAndOdd
checkEvenAndOdd = contramap (Even . getEvenAndOdd) checkEven
                  <> contramap (Odd . getEvenAndOdd) checkOdd
  • MFunctor: Changing the effect
import Data.List(isPrefixOf)
newtype Url = Url { getUrl :: String }

check404 :: Check () IO Url -- checks if the url returns 404

checkHttps :: Check' () Identity Url
checkHttps = ("https" `isPrefixOf`) ?>> ()

checkUrl :: Check () IO Url
checkUrl = check404 <> hoist generalize checkHttps

For more information see the README.

newtype Check (e :: Type) (m :: Type -> Type) (a :: Type) Source #

Constructors

Check 

Fields

Instances

Instances details
MFunctor (Check e :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Control.Validation.Check

Methods

hoist :: forall m n (b :: k). Monad m => (forall a. m a -> n a) -> Check e m b -> Check e n b #

Contravariant (Check e m) Source # 
Instance details

Defined in Control.Validation.Check

Methods

contramap :: (a -> b) -> Check e m b -> Check e m a #

(>$) :: b -> Check e m b -> Check e m a #

Applicative m => Divisible (Check e m) Source # 
Instance details

Defined in Control.Validation.Check

Methods

divide :: (a -> (b, c)) -> Check e m b -> Check e m c -> Check e m a #

conquer :: Check e m a #

Applicative m => Decidable (Check e m) Source # 
Instance details

Defined in Control.Validation.Check

Methods

lose :: (a -> Void) -> Check e m a #

choose :: (a -> Either b c) -> Check e m b -> Check e m c -> Check e m a #

Applicative m => Semigroup (Check e m a) Source # 
Instance details

Defined in Control.Validation.Check

Methods

(<>) :: Check e m a -> Check e m a -> Check e m a #

sconcat :: NonEmpty (Check e m a) -> Check e m a #

stimes :: Integral b => b -> Check e m a -> Check e m a #

Applicative m => Monoid (Check e m a) Source # 
Instance details

Defined in Control.Validation.Check

Methods

mempty :: Check e m a #

mappend :: Check e m a -> Check e m a -> Check e m a #

mconcat :: [Check e m a] -> Check e m a #

type Check' e = Check e Identity Source #

A Check with a trivial context

pass :: Applicative m => Check e m a Source #

The trivial Check that always succeeds.

passOnRight :: Applicative m => (a -> Either b ()) -> Check e m b -> Check e m a Source #

'passOnRight ignoreWhen check lets the argument pass when ignoreWhen returns `Right ()` and otherwise checks with check. It is a special case of choose from Decidable. It gives an example for how Checks expand to other datatypes since they are Divisible and Decidable, see generalizing a check to lists:

checkList :: Applicative m => Check e m a -> Check e m [a]
checkList c = passOnRight (\case
                            [] -> Right ()
                            x:xs -> Left (x, xs))
                          ( divide id c (checkList c))

mapError :: Functor m => (e -> e') -> Check e m a -> Check e' m a Source #

Mapping over the error type.

generalizeCheck :: Applicative m => Check' e a -> Check e m a Source #

Generalize a Check without context to any Check with an applicative context

validateBy :: Functor m => Check e m a -> Unvalidated a -> m (Either (Seq e) a) Source #

Validate Unvalidated data using a check.

validateBy' :: Check' e a -> Unvalidated a -> Either (Seq e) a Source #

validateBy for trivial context.

Constructing checks

The general way to construct a Check: Take the data to be checked and return a CheckResult.

Construction by predicates

checking :: (a -> m (CheckResult e)) -> Check e m a Source #

checking' :: (a -> CheckResult e) -> Check' e a Source #

Constructing a check from a predicate (if a prediceate returns True, the check passes) and a function constructing the error from the input. Naming conventions:

  • Functions that work on trivial contexts are postfixed by an apostrophe `'`.
  • Check constructors that discard the argument on error end with `_`.
  • All infix operators start with ? and end with > (So ?> is the "normal" version).
  • Additional >: discards its argument: ?>>, ?~>>.
  • Tilde works with non-trivial contexts: ?~>, ?~>>.

test :: Functor m => (a -> m Bool) -> (a -> e) -> Check e m a infix 7 Source #

(?~>) :: Functor m => (a -> m Bool) -> (a -> e) -> Check e m a infix 7 Source #

test' :: Applicative m => (a -> Bool) -> (a -> e) -> Check e m a infix 7 Source #

(?>) :: Applicative m => (a -> Bool) -> (a -> e) -> Check e m a infix 7 Source #

test_ :: Monad m => (a -> m Bool) -> e -> Check e m a infix 7 Source #

(?~>>) :: Monad m => (a -> m Bool) -> e -> Check e m a infix 7 Source #

test'_ :: Applicative m => (a -> Bool) -> e -> Check e m a infix 7 Source #

(?>>) :: Applicative m => (a -> Bool) -> e -> Check e m a infix 7 Source #

Lifting Checks

foldWithCheck :: (Foldable f, Applicative m) => Check e m a -> Check e m (f a) Source #

Lift a check to a foldable

traverseWithCheck :: (Traversable t, Applicative m) => Check e m a -> Check e m (t a) Source #

Lift a check to a traversable

For ADTs

A MultiCheck is a list of a list of checks, one for each field of each constructor. Do not be scared by the types but read the section in the README | A Multi-Check for an ADT, one 'Check e m' for each field of each constructor, organized in Lists (see examples for construction)

type MultiCheck e m a = NP (NP (Check e m)) (Code a) Source #

joinMultiCheck :: forall a m e. (Applicative m, Generic a) => MultiCheck e m a -> Check e m a Source #

Combine all Checks from a MultiCheck into a single Check for the datatype a (given it has a Generic instance).

mapErrorsWithInfo :: forall e e' a m. (Functor m, HasDatatypeInfo a) => Proxy a -> (DatatypeName -> ConstructorName -> FieldName -> e -> e') -> MultiCheck e m a -> MultiCheck e' m a Source #

Examples

Expand
checkNotEmpty = not . null ?>> "No name given"
checkAdult  = (>= 18) ?> printf "%s is too young; must be at least 18 years old" . show

checkHttps = ("https://" `isPrefixOf`) ?> printf "Website '%s' is not secure: Missing 'https'"

checkPet :: Check' Err Pet
checkPet = joinMultiCheck
(  (checkNotEmpty :* mempty :* Nil) -- checks for the fields of the first constructor
:* (checkNotEmpty :* Nil) -- checks for the fields of the second constructor
:* Nil ) -- outer list is also terminated by `Nil`

checkProfile :: Check' Err Profile
checkProfile = joinMultiCheck
(  checkNotEmpty
:* checkAdult
:* checkPet
:* foldWithCheck checkHttps -- `foldWithCheck` lifts a `Check` to a `Foldable`, in this case a list
:* Nil ) -- only one constructor, so the outer list is a singleton list
:* Nil

Change the error of a MultiCheck using the information about the datatype.

constructorCheck Source #

Arguments

:: forall a m e xs. (Applicative m, Generic a) 
=> (NP (Check e m) xs -> NS (NP (Check e m)) (Code a))

The function deciding the constructor, Z for the zeroth, 'S . Z' for the first, etc.

-> NP (Check e m) xs

Product of Checkes, one for each constructor

-> Check e m a 

Make a Check for that only checks a single constructor by suppling a list containing a Check for each field

Reexports

General

hoist :: forall m n (b :: k). (MFunctor t, Monad m) => (forall a. m a -> n a) -> t m b -> t n b #

Lift a monad morphism from m to n into a monad morphism from (t m) to (t n)

The first argument to hoist must be a monad morphism, even though the type system does not enforce this

contramap :: Contravariant f => (a -> b) -> f b -> f a #

SOP

data NP (a :: k -> Type) (b :: [k]) where #

An n-ary product.

The product is parameterized by a type constructor f and indexed by a type-level list xs. The length of the list determines the number of elements in the product, and if the i-th element of the list is of type x, then the i-th element of the product is of type f x.

The constructor names are chosen to resemble the names of the list constructors.

Two common instantiations of f are the identity functor I and the constant functor K. For I, the product becomes a heterogeneous list, where the type-level list describes the types of its components. For K a, the product becomes a homogeneous list, where the contents of the type-level list are ignored, but its length still specifies the number of elements.

In the context of the SOP approach to generic programming, an n-ary product describes the structure of the arguments of a single data constructor.

Examples:

I 'x'    :* I True  :* Nil  ::  NP I       '[ Char, Bool ]
K 0      :* K 1     :* Nil  ::  NP (K Int) '[ Char, Bool ]
Just 'x' :* Nothing :* Nil  ::  NP Maybe   '[ Char, Bool ]

Constructors

Nil :: forall k (a :: k -> Type). NP a ('[] :: [k]) 
(:*) :: forall k (a :: k -> Type) (x :: k) (xs :: [k]). a x -> NP a xs -> NP a (x ': xs) infixr 5 

Instances

Instances details
HTrans (NP :: (k1 -> Type) -> [k1] -> Type) (NP :: (k2 -> Type) -> [k2] -> Type) 
Instance details

Defined in Data.SOP.NP

Methods

htrans :: forall c (xs :: l1) (ys :: l2) proxy f g. AllZipN (Prod NP) c xs ys => proxy c -> (forall (x :: k10) (y :: k20). c x y => f x -> g y) -> NP f xs -> NP g ys #

hcoerce :: forall (f :: k10 -> Type) (g :: k20 -> Type) (xs :: l1) (ys :: l2). AllZipN (Prod NP) (LiftedCoercible f g) xs ys => NP f xs -> NP g ys #

HSequence (NP :: (k -> Type) -> [k] -> Type) 
Instance details

Defined in Data.SOP.NP

Methods

hsequence' :: forall (xs :: l) f (g :: k0 -> Type). (SListIN NP xs, Applicative f) => NP (f :.: g) xs -> f (NP g xs) #

hctraverse' :: forall c (xs :: l) g proxy f f'. (AllN NP c xs, Applicative g) => proxy c -> (forall (a :: k0). c a => f a -> g (f' a)) -> NP f xs -> g (NP f' xs) #

htraverse' :: forall (xs :: l) g f f'. (SListIN NP xs, Applicative g) => (forall (a :: k0). f a -> g (f' a)) -> NP f xs -> g (NP f' xs) #

HTraverse_ (NP :: (k -> Type) -> [k] -> Type) 
Instance details

Defined in Data.SOP.NP

Methods

hctraverse_ :: forall c (xs :: l) g proxy f. (AllN NP c xs, Applicative g) => proxy c -> (forall (a :: k0). c a => f a -> g ()) -> NP f xs -> g () #

htraverse_ :: forall (xs :: l) g f. (SListIN NP xs, Applicative g) => (forall (a :: k0). f a -> g ()) -> NP f xs -> g () #

HCollapse (NP :: (k -> Type) -> [k] -> Type) 
Instance details

Defined in Data.SOP.NP

Methods

hcollapse :: forall (xs :: l) a. SListIN NP xs => NP (K a) xs -> CollapseTo NP a #

HAp (NP :: (k -> Type) -> [k] -> Type) 
Instance details

Defined in Data.SOP.NP

Methods

hap :: forall (f :: k0 -> Type) (g :: k0 -> Type) (xs :: l). Prod NP (f -.-> g) xs -> NP f xs -> NP g xs #

HPure (NP :: (k -> Type) -> [k] -> Type) 
Instance details

Defined in Data.SOP.NP

Methods

hpure :: forall (xs :: l) f. SListIN NP xs => (forall (a :: k0). f a) -> NP f xs #

hcpure :: forall c (xs :: l) proxy f. AllN NP c xs => proxy c -> (forall (a :: k0). c a => f a) -> NP f xs #

All (Compose Eq f) xs => Eq (NP f xs) 
Instance details

Defined in Data.SOP.NP

Methods

(==) :: NP f xs -> NP f xs -> Bool #

(/=) :: NP f xs -> NP f xs -> Bool #

(All (Compose Eq f) xs, All (Compose Ord f) xs) => Ord (NP f xs) 
Instance details

Defined in Data.SOP.NP

Methods

compare :: NP f xs -> NP f xs -> Ordering #

(<) :: NP f xs -> NP f xs -> Bool #

(<=) :: NP f xs -> NP f xs -> Bool #

(>) :: NP f xs -> NP f xs -> Bool #

(>=) :: NP f xs -> NP f xs -> Bool #

max :: NP f xs -> NP f xs -> NP f xs #

min :: NP f xs -> NP f xs -> NP f xs #

All (Compose Show f) xs => Show (NP f xs) 
Instance details

Defined in Data.SOP.NP

Methods

showsPrec :: Int -> NP f xs -> ShowS #

show :: NP f xs -> String #

showList :: [NP f xs] -> ShowS #

All (Compose Semigroup f) xs => Semigroup (NP f xs)

Since: sop-core-0.4.0.0

Instance details

Defined in Data.SOP.NP

Methods

(<>) :: NP f xs -> NP f xs -> NP f xs #

sconcat :: NonEmpty (NP f xs) -> NP f xs #

stimes :: Integral b => b -> NP f xs -> NP f xs #

(All (Compose Monoid f) xs, All (Compose Semigroup f) xs) => Monoid (NP f xs)

Since: sop-core-0.4.0.0

Instance details

Defined in Data.SOP.NP

Methods

mempty :: NP f xs #

mappend :: NP f xs -> NP f xs -> NP f xs #

mconcat :: [NP f xs] -> NP f xs #

All (Compose NFData f) xs => NFData (NP f xs)

Since: sop-core-0.2.5.0

Instance details

Defined in Data.SOP.NP

Methods

rnf :: NP f xs -> () #

type AllZipN (NP :: (k -> Type) -> [k] -> Type) (c :: a -> b -> Constraint) 
Instance details

Defined in Data.SOP.NP

type AllZipN (NP :: (k -> Type) -> [k] -> Type) (c :: a -> b -> Constraint) = AllZip c
type Same (NP :: (k1 -> Type) -> [k1] -> Type) 
Instance details

Defined in Data.SOP.NP

type Same (NP :: (k1 -> Type) -> [k1] -> Type) = NP :: (k2 -> Type) -> [k2] -> Type
type SListIN (NP :: (k -> Type) -> [k] -> Type) 
Instance details

Defined in Data.SOP.NP

type SListIN (NP :: (k -> Type) -> [k] -> Type) = SListI :: [k] -> Constraint
type UnProd (NP :: (k -> Type) -> [k] -> Type) 
Instance details

Defined in Data.SOP.NS

type UnProd (NP :: (k -> Type) -> [k] -> Type) = NS :: (k -> Type) -> [k] -> Type
type Prod (NP :: (k -> Type) -> [k] -> Type) 
Instance details

Defined in Data.SOP.NP

type Prod (NP :: (k -> Type) -> [k] -> Type) = NP :: (k -> Type) -> [k] -> Type
type AllN (NP :: (k -> Type) -> [k] -> Type) (c :: k -> Constraint) 
Instance details

Defined in Data.SOP.NP

type AllN (NP :: (k -> Type) -> [k] -> Type) (c :: k -> Constraint) = All c
type CollapseTo (NP :: (k -> Type) -> [k] -> Type) a 
Instance details

Defined in Data.SOP.NP

type CollapseTo (NP :: (k -> Type) -> [k] -> Type) a = [a]

type DatatypeName = String #

The name of a datatype.

type ConstructorName = String #

The name of a data constructor.

type FieldName = String #

The name of a field / record selector.