packages feed

test-fun-0.1.0.0: README.md

# Testable functions

A representation of functions for property testing, featuring
random generation, shrinking, and printing.

This package implements the core functionality.
Separate packages integrate it with existing testing frameworks.

- QuickCheck: [quickcheck-higherorder](https://github.com/Lysxia/quickcheck-higherorder)

- Hedgehog: [hedgehog-higherorder](https://github.com/Lysxia/hedgehog-higherorder)

## Summary

This package defines a type of *testable functions* `a :-> r`,
representing functions `a -> r`.

- To interpret a testable function into a function `a -> r`,
  use `applyFun :: (a :-> r) -> a -> r`.

- To pretty-print a testable function,
  use `show :: Show r => (a :-> r) -> String`.

- To shrink a testable function, given a shrinker for `r`,
  use `shrinkFun :: (r -> [r]) -> (a :-> r) -> [a :-> r]`.

- To randomly generate a testable function `a :-> r`,
  apply a *cogenerator* of `a` to a generator of `r`.
  Cogenerators can be defined using combinators from this library.

### Cogenerators

The type of cogenerators of `a` is `Co Gen a r`,
where `Gen` is QuickCheck's monad of random generators
and `r` is an abstract parameter (it's really `forall r. Co Gen a r`).

That type `Co Gen a r` is literally defined as a type synonym of
`Gen r -> Gen (a :-> r)`.
Given both a cogenerator `c :: Co Gen a r`, and a generator `g :: Gen r`,
we can construct the generator of testable functions `c g :: Gen (a :-> r)`.

(Users can just think of `Co Gen` as a whole,
even though the implementation defines a more general `Co`
which may be applied to any monad.
Similarly, the parameter `r` can be ignored most of the time;
it matters to cogenerators of parameterized types.)

There are several combinators to define cogenerators,
covering the following scenarios.

#### Newtypes and embeddings

If we have a newtype `A` around some old type `B`, and we also have
a cogenerator of `B`:

```haskell
newtype A = MkA { unA :: B }

cogenB :: Co Gen B r
```

Then `cogenEmbed` transforms `cogenB` into a cogenerator of `A`:

```haskell
cogenEmbed "unA" unA cogenB :: Co Gen A r
```

This is actually not restricted to newtypes:
any "embedding" function `A -> B` (here, `unA`) can be used to convert a
`Co Gen B r` to a `Co Gen A r`.
(Yes, there is a contravariant functor hiding there.)
Note that `cogenEmbed` expects a name for that function as a `String`
in its first argument, for pretty-printing.

#### Generic data types

To define a cogenerator of a type which is an instance of `Generic` (from
`GHC.Generics`), use `cogenGeneric`. For example, consider this type:

```haskell
data Small a = Zero | One a | Two a a
  deriving Generic
```

The function `cogenGeneric` takes a heterogeneous list of
cogenerators, one for each constructor of the generic type.
This is `cs` in the example below.

The heterogeneous list is constructed using `(:+)` to append
elements and `()` for the end of the list.

For constructors with multiple fields,
use `(.)` to compose cogenerators for individual fields.

For nullary constructors, use `id` as the "nullary cogenerator".

```haskell
cogenSmall ::
  forall a.
  (forall r. Co Gen a r) ->
  (forall r. Co Gen (Small a) r)
cogenSmall cogenA = cogenGeneric cs where
  cs
    =  id                 -- Nullary cogenerator, for the constructor Zero
    :+ cogenA             -- Cogenerator of a, for the constructor One
    :+ (cogenA . cogenA)  -- A cogenerator of a, once for each field of the constructor Two
    :+ ()                 -- End of the list
```

#### Functions

To generate higher-order testable functions `(a -> b) :-> r`,
we need a cogenerator of functions `a -> b`,
which we can define using `cogenFun`.

To a first approximation, the function `cogenFun` transforms
a cogenerator of `b` into a cogenerator of `(a -> b)`, provided
a way to generate, shrink, and show `a`.

This is actually generalized further by allowing one to provide
a way to generate, shrink, and show a *representation* `a0` of `a`,
which can be equal to `a` in simple cases,
but this generalization makes it possible to generate
functions of arbitrarily high order.

Hence, to construct a cogenerator of `a -> b`,
the function `cogenFun` takes the following arguments, in this order:

1. `Concrete a0`: a dictionary containing a shrinker and a pretty-printer of
   representations `a0`;
2. `Gen (Maybe a0)`: a random generator of `a0`, it must generate `Nothing`
   once in a while (say with probability 1/5 if you have no clue);
3. `a0 -> a`: a function from representations to actual values
   (`id` in simple cases);
4. `forall r. Co Gen b r`: a cogenerator of `b`.

## References

- [*Shrinking and showing functions*](https://dl.acm.org/citation.cfm?id=2364516),
  by Koen Claessen, Haskell Symposium 2012.

  This package extends that work with support for higher-order functions.

  Other implementations based on that paper can be found in:

  + [QuickCheck](https://hackage.haskell.org/package/QuickCheck-2.13.2); in
    particular see the [`Fun`
    type](https://hackage.haskell.org/package/QuickCheck-2.13.2/docs/Test-QuickCheck.html#g:14).

  + [hedgehog-fn](https://hackage.haskell.org/package/hedgehog-fn).

## Internal module policy

Modules under `Test.Fun.Internal` are not subject to any versioning policy.
Breaking changes may apply to them at any time.

If something in those modules seems useful, please report it or create a pull
request to export it from an external module.