| Copyright | (c) Fabian Birkmann 2020 |
|---|---|
| License | GPL-3 |
| Maintainer | 99fabianb@sis.gl |
| Stability | experimental |
| Portability | POSIX |
| Safe Haskell | None |
| Language | Haskell2010 |
Control.Validation.Check
Contents
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 documentation see the (README)[https:/gitlab.comBirkmannvalidation-check-blobmaster/README.md].
Synopsis
- newtype Unvalidated (a :: Type) = Unvalidated {
- unsafeValidate :: a
- unvalidated :: a -> Unvalidated a
- data CheckResult (e :: Type)
- checkResult :: a -> (Seq e -> a) -> CheckResult e -> a
- failsWith :: e -> CheckResult e
- failsNoMsg :: CheckResult e
- passed :: CheckResult e -> Bool
- failed :: CheckResult e -> Bool
- checkResultToEither :: a -> CheckResult e -> Either (Seq e) a
- newtype Check (e :: Type) (m :: Type -> Type) (a :: Type) = Check {
- runCheck :: Unvalidated a -> m (CheckResult e)
- type Check' e = Check e Identity
- passOnRight :: Applicative m => (a -> Either b ()) -> Check e m b -> Check e m a
- mapError :: Functor m => (e -> e') -> Check e m a -> Check e' m a
- generalizeCheck :: Applicative m => Check' e a -> Check e m a
- validateBy :: Functor m => Check e m a -> Unvalidated a -> m (Either (Seq e) a)
- validateBy' :: Check' e a -> Unvalidated a -> Either (Seq e) a
- checking :: (a -> m (CheckResult e)) -> Check e m a
- checking' :: (a -> CheckResult e) -> Check' e a
- test :: Functor m => (a -> m Bool) -> (a -> e) -> Check e m a
- (?~>) :: Functor m => (a -> m Bool) -> (a -> e) -> Check e m a
- test' :: Applicative m => (a -> Bool) -> (a -> e) -> Check e m a
- (?>) :: Applicative m => (a -> Bool) -> (a -> e) -> Check e m a
- test_ :: Monad m => (a -> m Bool) -> e -> Check e m a
- (?~>>) :: Monad m => (a -> m Bool) -> e -> Check e m a
- test'_ :: Applicative m => (a -> Bool) -> e -> Check e m a
- (?>>) :: Applicative m => (a -> Bool) -> e -> Check e m a
- foldWithCheck :: (Foldable f, Applicative m) => Check e m a -> Check e m (f a)
- traverseWithCheck :: (Traversable t, Applicative m) => Check e m a -> Check e m (t a)
- hoist :: (MFunctor t, Monad m) => (forall a. m a -> n a) -> t m b -> t n b
- contramap :: Contravariant f => (a -> b) -> f b -> f a
Unvalidated values
A newtype around unvalidated values so one cannot use the value until it is validated.
You can create an Unvalidated via unvalidated, but it is often more convient
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
| Unvalidated | |
Fields
| |
Instances
unvalidated :: a -> Unvalidated a Source #
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 #
Instances
checkResult :: a -> (Seq e -> a) -> CheckResult e -> a Source #
A fold for CheckResult
failsWith :: e -> CheckResult e Source #
failsNoMsg :: CheckResult e Source #
Throwing an error without a message.
passed :: CheckResult e -> Bool Source #
failed :: CheckResult e -> Bool 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 prefixed by a `'`.
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 checkHttpsFor more information see the README.
newtype Check (e :: Type) (m :: Type -> Type) (a :: Type) Source #
Constructors
| Check | |
Fields
| |
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 Nothing 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 #
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 #
Constructing checks
Constructing a check from a predicate. Naming conventions:
- Functions that work on trivial contexts are prefixed 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:
?~>,?~>>.
checking :: (a -> m (CheckResult e)) -> Check e m a Source #
checking' :: (a -> CheckResult e) -> Check' e a Source #
Helper for deriving Checkable
foldWithCheck :: (Foldable f, Applicative m) => Check e m a -> Check e m (f a) Source #
Lifting checks
traverseWithCheck :: (Traversable t, Applicative m) => Check e m a -> Check e m (t a) Source #
Reexports
hoist :: (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 #