test-fun- Testable functions

Safe HaskellSafe




Random generation of higher-order functions.


This is an internal module: it is not subject to any versioning policy, breaking changes can happen at any time. It is made available only for debugging. Otherwise, use Test.Fun.

If something here seems useful, please open an issue to export it from an external module.

Fun fact

This module only uses an Applicative constraint on the type of generators (which is really QuickCheck's Gen).



type Co gen a r = gen r -> gen (a :-> r) Source #

A "cogenerator" of a is a random generator of functions with domain a. They are parameterized by a generator in the codomain r.

More generally, we can make cogenerators to generate functions of arbitrary arities; Co gen a r is only the type of unary cogenerators.

gen r -> gen (a :-> r)         -- Co gen a r
gen r -> gen (a :-> b :-> r)
gen r -> gen (a :-> b :-> c :-> r)
gen r -> gen (a :-> b :-> c :-> d :-> r)

-- etc.

More details


Cogenerators can be composed using id and (.) (the usual combinators for functions). The arity of a cogenerator f . g is the sum of the arities of f and g.

id  ::  forall r. gen r -> gen r  -- 0-ary cogenerator

-- (1-ary) . (1-ary) = (2-ary)
(.) :: (forall r. gen r -> gen (a :-> r)) ->
       (forall r. gen r -> gen (b :-> r)) ->
       (forall r. gen r -> gen (a :-> b :-> r))

-- (2-ary) . (1-ary) = (3-ary)
(.) :: (forall r. gen r -> gen (a :-> b :-> r)) ->
       (forall r. gen r -> gen (c :-> r)) ->
       (forall r. gen r -> gen (a :-> b :-> c :-> r))

Note: the last type parameter r should really be universally quantified (as in the above pseudo type signatures), but instead we use more specialized types to avoid making types higher-ranked.

cogenEmbed :: Functor gen => FunName -> (a -> b) -> Co gen b r -> Co gen a r Source #

Cogenerator for a type a from a cogenerator for b, given an embedding function (a -> b), and a name for that function (used for pretty-printing).



The common usage is to construct cogenerators for newtypes.

-- Given some cogenerator of Fruit
cogenFruit :: Co Gen Fruit r

-- Wrap Fruit in a newtype
newtype Apple = Apple { unApple :: Fruit }

cogenApple :: Co Gen Apple r
cogenApple = cogenEmbed "unApple" cogenFruit

If cogenFruit generates a function that looks like:

\y -> case y :: Fruit of { ... }

then cogenApple will look like this, where y is replaced with unApple x:

\x -> case unApple x :: Fruit of { ... }

cogenIntegral :: (Applicative gen, Integral a) => TypeName -> Co gen a r Source #

Cogenerator for an integral type. The name of the type is used for pretty-printing.


cogenInteger :: Co Gen Integer r
cogenInteger = cogenIntegral "Integer"

cogenInt :: Co Gen Int r
cogenInt = cogenIntegral "Int"

cogenWord :: Co Gen Word r
cogenWord = cogenIntegral "Word"

cogenIntegral' :: Applicative gen => TypeName -> (a -> Integer) -> Co gen a r Source #

Variant of cogenIntegral with an explicit conversion to Integer.

genBin :: Applicative gen => gen r -> gen (Bin r) Source #

cogenApply Source #


:: Functor gen 
=> Concrete a0

Shrink and show a0.

-> (a0 -> a)

Reify to value a (id for simple data types).

-> a0

Value to inspect.

-> gen (b :-> ((a -> b) :-> r))

Cogenerator of b

-> gen ((a -> b) :-> r) 

Extend a cogenerator of functions (a -> b) (i.e., a generator of higher-order functions ((a -> b) -> r)), applying the function to a given value a and inspecting the result with a cogenerator of b.

This is parameterized by a way to generate, shrink, and show values of type a or, more generally, some representation a0 of values of type a.


-- Assume Chips is some concrete type.
concreteChips :: Concrete Chips

-- Assume we have a cogenerator of Fish.
cogenFish :: forall r. Gen r -> Gen (Fish :-> r)

-- Then we can use cogenApply to construct this function
-- to transform cogenerators of functions (Chips -> Fish).
cogenX :: forall r.
  Chips ->
  Gen ((Chips -> Fish) :-> r) ->
  Gen ((Chips -> Fish) :-> r)
cogenX = cogenApply concreteChips id . cogenFish

-- If we have some inputs...
chips1, chips2, chips3 :: Chips

-- ... we can construct a cogenerator of functions by iterating cogenX.
cogenF :: forall r. Gen r -> Gen ((Chips -> Fish) :-> r)
cogenF = cogenX chips1 . cogenX chips2 . cogenX chips3 . cogenConst

cogenConst :: Functor gen => Co gen a r Source #

The trivial cogenerator which generates a constant function.

cogenFun Source #


:: Monad gen 
=> Concrete a0

Shrink and show a0.

-> gen (Maybe a0)

Generate representations of argument values.

-> (a0 -> a)

Interpret a representation a0 into a value a (id for simple data types).

-> Co gen b ((a -> b) :-> r)

Cogenerator of b.

-> Co gen (a -> b) r 

Construct a cogenerator of functions (a -> b) from a cogenerator of b, using gen (Maybe a0) to generate random arguments until it returns Nothing.