Safe Haskell | None |
---|---|
Language | Haskell2010 |
The general procedure for generating functions of type A -> B
looks something like this:
{-# language DeriveGeneric #-} {-# language TypeApplications #-} import Hedgehog import Hedgehog.Function data A = ... deriving (Generic, ...) instance Arg A instance Vary A genB :: MonadGen m => m B genB = ... prop_test :: Property prop_test = property $ do f <- forAllFn $ fn @A genB ...
Here's an example of how to use the library to test the "fmap composition" law.
ScopedTypeVariables
and TypeApplications
are recommended for ease of use. RankNTypes
is only necessary for this example.
{-# language RankNTypes #-} {-# language ScopedTypeVariables, TypeApplications #-} import Hedgehog import Hedgehog.Function import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range map_compose :: forall f a b c . ( Functor f , Show (f a) , Show a, Arg a, Vary a , Show b, Arg b, Vary b , Show c , Eq (f c) , Show (f c) ) => (forall x. Gen x -> Gen (f x)) -> Gen a -> Gen b -> Gen c -> Property map_compose genF genA genB genC = property $ do g <- forAllFn $ fn @a genB f <- forAllFn $ fn @b genC xs <- forAll $ genF genA fmap (f . g) xs === fmap f (fmap g xs) prop_map_list :: Property prop_map_list = map_compose (Gen.list (Range.constant 0 100)) Gen.bool Gen.bool Gen.bool
- module GHC.Generics
- data Fn a b
- forAllFn :: (Show a, Show b, Monad m) => Gen (Fn a b) -> PropertyT m (a -> b)
- apply :: Fn a b -> a -> b
- fn :: (Arg a, Vary a) => Gen b -> Gen (Fn a b)
- fnWith :: Arg a => CoGen a -> Gen b -> Gen (Fn a b)
- gbuild :: (Generic a, GArg (Rep a)) => (a -> c) -> a :-> c
- via :: Arg b => (a -> b) -> (b -> a) -> (a -> c) -> a :-> c
- buildIntegral :: (Arg a, Integral a) => (a -> c) -> a :-> c
- class Arg a where
- module Data.Functor.Contravariant
- module Data.Functor.Contravariant.Divisible
- data CoGenT m a
- type CoGen = CoGenT Identity
- gvary :: (Generic a, GVary (Rep a)) => CoGenT m a
- varyIntegral :: Integral a => CoGenT m a
- class Vary a where
Documentation
module GHC.Generics
The type of randomly-generated functions
Generation
forAllFn :: (Show a, Show b, Monad m) => Gen (Fn a b) -> PropertyT m (a -> b) Source #
Run the function generator to retrieve a function
fnWith :: Arg a => CoGen a -> Gen b -> Gen (Fn a b) Source #
Generate a function using the user-supplied co-generator
Building
gbuild :: (Generic a, GArg (Rep a)) => (a -> c) -> a :-> c Source #
Reify a function whose domain has an instance of Generic
via :: Arg b => (a -> b) -> (b -> a) -> (a -> c) -> a :-> c Source #
Reify a function via an isomorphism.
If your function's domain has no instance of Generic
then you can still reify it using
an isomorphism to a better domain type. For example, the Arg
instance for Integral
uses an isomorphism from Integral a => a
to (Bool, [Bool])
, where the first element
is the sign, and the second element is the bit-string.
Note: via f g
will only be well-behaved if g . f = id
and f . g = id
instance Arg A where
allows functions which take A
s to be reified
build :: (a -> c) -> a :-> c Source #
build :: (Generic a, GArg (Rep a)) => (a -> c) -> a :-> c Source #
Arg Bool Source # | |
Arg Int Source # | |
Arg Int8 Source # | |
Arg Int16 Source # | |
Arg Int32 Source # | |
Arg Int64 Source # | |
Arg Integer Source # | |
Arg Ordering Source # | |
Arg () Source # | |
Arg Void Source # | |
Arg a => Arg [a] Source # | |
Arg a => Arg (Maybe a) Source # | |
(Arg a, Arg b) => Arg (Either a b) Source # | |
(Arg a, Arg b) => Arg (a, b) Source # | |
Varying
module Data.Functor.Contravariant
A
is used to perturb a CoGenT
m a
based on the value of the GenT
m ba
. This way,
the generated function will have a varying (but still deterministic) right hand side.
Co-generators can be built using Divisible
and Decidable
, but it is recommended to
derive Generic
and use the default instance of the Vary
type class.
CoGenT
m ~Op
(Endo
(GenT
m b))
gvary :: (Generic a, GVary (Rep a)) => CoGenT m a Source #
Build a co-generator for a type which has a Generic
instance
Vary
provides a canonical co-generator for a type.
While technically there are many possible co-generators for a given type, we don't get any benefit from caring.
Vary Bool Source # | |
Vary Int Source # | |
Vary Int8 Source # | |
Vary Int16 Source # | |
Vary Int32 Source # | |
Vary Int64 Source # | |
Vary Integer Source # | |
Vary Ordering Source # | |
Vary Word8 Source # | |
Vary () Source # | |
Vary Void Source # | |
Vary a => Vary [a] Source # | |
Vary a => Vary (Maybe a) Source # | |
(Vary a, Vary b) => Vary (Either a b) Source # | |
(Vary a, Vary b) => Vary (a, b) Source # | |