Copyright | (c) Fabian Birkmann 2020 |
---|---|
License | GPL-3 |
Maintainer | 99fabianb@sis.gl |
Stability | experimental |
Portability | POSIX |
Safe Haskell | None |
Language | Haskell2010 |
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 #
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 #
:: 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 checkHttps
For more information see the README.
newtype Check (e :: Type) (m :: Type -> Type) (a :: Type) Source #
Check | |
|
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 Check
s 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 #