{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
module Test.Validity.Applicative
( applicativeSpecOnValid
, applicativeSpec
, applicativeSpecOnArbitrary
, applicativeSpecOnGens
) where
import Data.Data
import Data.GenValidity
import Test.Hspec
import Test.QuickCheck
import Test.Validity.Functions
import Test.Validity.Utils
{-# ANN module "HLint: ignore Avoid lambda" #-}
pureTypeStr ::
forall (f :: * -> *). (Typeable f)
=> String
pureTypeStr = unwords ["pure", "::", "a", "->", nameOf @f, "a"]
seqTypeStr ::
forall (f :: * -> *). (Typeable f)
=> String
seqTypeStr =
unwords
[ "(<*>)"
, "::"
, nameOf @f
, "(a"
, "->"
, "b)"
, "->"
, nameOf @f
, "a"
, "->"
, nameOf @f
, "b"
]
seqrTypeStr ::
forall (f :: * -> *). (Typeable f)
=> String
seqrTypeStr =
unwords
[ "(*>)"
, "::"
, nameOf @f
, "a"
, "->"
, nameOf @f
, "b"
, "->"
, nameOf @f
, "b"
]
seqlTypeStr ::
forall (f :: * -> *). (Typeable f)
=> String
seqlTypeStr =
unwords
[ "(<*)"
, "::"
, nameOf @f
, "a"
, "->"
, nameOf @f
, "b"
, "->"
, nameOf @f
, "a"
]
applicativeSpecOnValid ::
forall (f :: * -> *).
(Eq (f Int), Show (f Int), Applicative f, Typeable f, GenValid (f Int))
=> Spec
applicativeSpecOnValid = applicativeSpecWithInts @f genValid
applicativeSpec ::
forall (f :: * -> *).
( Eq (f Int)
, Show (f Int)
, Applicative f
, Typeable f
, GenUnchecked (f Int)
)
=> Spec
applicativeSpec = applicativeSpecWithInts @f genUnchecked
applicativeSpecOnArbitrary ::
forall (f :: * -> *).
(Eq (f Int), Show (f Int), Applicative f, Typeable f, Arbitrary (f Int))
=> Spec
applicativeSpecOnArbitrary = applicativeSpecWithInts @f arbitrary
applicativeSpecWithInts ::
forall (f :: * -> *).
(Show (f Int), Eq (f Int), Applicative f, Typeable f)
=> Gen (f Int)
-> Spec
applicativeSpecWithInts gen =
applicativeSpecOnGens
@f
@Int
genUnchecked
"int"
gen
(unwords [nameOf @f, "of ints"])
gen
(unwords [nameOf @f, "of ints"])
((+) <$> genUnchecked)
"increments"
(pure <$> ((+) <$> genUnchecked))
(unwords [nameOf @f, "of increments"])
(pure <$> ((*) <$> genUnchecked))
(unwords [nameOf @f, "of scalings"])
applicativeSpecOnGens ::
forall (f :: * -> *) (a :: *) (b :: *) (c :: *).
( Show a
, Eq a
, Show (f a)
, Eq (f a)
, Show (f b)
, Eq (f b)
, Show (f c)
, Eq (f c)
, Applicative f
, Typeable f
, Typeable a
, Typeable b
, Typeable c
)
=> Gen a
-> String
-> Gen (f a)
-> String
-> Gen (f b)
-> String
-> Gen (a -> b)
-> String
-> Gen (f (a -> b))
-> String
-> Gen (f (b -> c))
-> String
-> Spec
applicativeSpecOnGens gena genaname gen genname genb genbname genfa genfaname genffa genffaname genffb genffbname =
parallel $
describe ("Applicative " ++ nameOf @f) $ do
describe (unwords [pureTypeStr @f, "and", seqTypeStr @f]) $ do
it
(unwords
[ "satisfy the identity law: 'pure id <*> v = v' for"
, genDescr @(f a) genname
]) $
equivalentOnGen (pure id <*>) id gen shrinkNothing
it
(unwords
[ "satisfy the composition law: 'pure (.) <*> u <*> v <*> w = u <*> (v <*> w)' for"
, genDescr @(f (b -> c)) genffbname
, "composed with"
, genDescr @(f (a -> b)) genffaname
, "and applied to"
, genDescr @(f a) genname
]) $
equivalentOnGens3
(\(Anon u) (Anon v) w ->
pure (.) <*> (u :: f (b -> c)) <*> (v :: f (a -> b)) <*>
(w :: f a) :: f c)
(\(Anon u) (Anon v) w -> u <*> (v <*> w) :: f c)
((,,) <$> (Anon <$> genffb) <*> (Anon <$> genffa) <*> gen)
shrinkNothing
it
(unwords
[ "satisfy the homomorphism law: 'pure f <*> pure x = pure (f x)' for"
, genDescr @(a -> b) genfaname
, "sequenced with"
, genDescr @a genaname
]) $
equivalentOnGens2
(\(Anon f) x -> pure f <*> pure x :: f b)
(\(Anon f) x -> pure $ f x :: f b)
((,) <$> (Anon <$> genfa) <*> gena)
shrinkNothing
it
(unwords
[ "satisfy the interchange law: 'u <*> pure y = pure ($ y) <*> u' for"
, genDescr @(f (a -> b)) genffaname
, "sequenced with"
, genDescr @a genaname
]) $
equivalentOnGens2
(\(Anon u) y -> u <*> pure y :: f b)
(\(Anon u) y -> pure ($ y) <*> u :: f b)
((,) <$> (Anon <$> genffa) <*> gena)
shrinkNothing
it
(unwords
[ "satisfy the law about the functor instance: fmap f x = pure f <*> x for"
, genDescr @(a -> b) genfaname
, "mapped over"
, genDescr @(f a) genname
]) $
equivalentOnGens2
(\(Anon f) x -> fmap f x)
(\(Anon f) x -> pure f <*> x)
((,) <$> (Anon <$> genfa) <*> gen)
shrinkNothing
describe (seqrTypeStr @f) $
it
(unwords
[ "is equivalent to its default implementation 'u *> v = pure (const id) <*> u <*> v' for"
, genDescr @(f a) genname
, "in front of"
, genDescr @b genbname
]) $
equivalentOnGens2
(\u v -> u *> v)
(\u v -> pure (const id) <*> u <*> v)
((,) <$> gen <*> genb)
shrinkNothing
describe (seqlTypeStr @f) $
it
(unwords
[ "is equivalent to its default implementation 'u <* v = pure const <*> u <*> v' for"
, genDescr @b genbname
, "behind"
, genDescr @(f a) genname
]) $
equivalentOnGens2
(\u v -> u <* v)
(\u v -> pure const <*> u <*> v)
((,) <$> gen <*> genb)
shrinkNothing