hedgehog-classes
Hedgehog will eat your typeclass bugs
https://github.com/hedgehogqa/haskell-hedgehog-classes
LTS Haskell 23.1: | 0.2.5.4@rev:5 |
Stackage Nightly 2024-12-27: | 0.2.5.4@rev:5 |
Latest on Hackage: | 0.2.5.4@rev:5 |
hedgehog-classes-0.2.5.4@sha256:d757e915c7a29a81368e74e1b75a37e89a71b525168cc608647b7eddd29a5da6,6052
Module documentation for 0.2.5.4
- Hedgehog
hedgehog-classes
Hedgehog will eat your typeclass bugs.
Motivation
hedgehog-classes
is a wrapper around Hedgehog that aims to provide a simple, straightforward API for testing common typeclass laws quickly, while providing good error messages to help debug any failing tests. It is inspired by the quickcheck-classes library.
API Overview
The API of hedgehog-classes
is dead simple. There are three parts.
The first part is a datatype, called ‘Laws’, which looks like this:
data Laws = Laws
{ lawsTypeclass :: String
, lawsProperties :: [(String,Property)]
}
It is a typeclass name along with a list of named property tests.
The second part of hedgehog-classes
are the functions, which follow a simple structure. All functions in hedgehog-classes
have one of the following three type signatures, based on the kind of the type which the corresponding typeclass parameterises (Nullary, Unary, or Binary). Note that they all return a ‘Laws’, only the inputs are different. Below, ‘Ctx’ refers to the typeclass in question:
-- Typeclasses that have kind 'Type -> Constraint', e.g. 'Eq'
tcLaw :: (Ctx a, Eq a, Show a) => Gen a -> Laws
-- Typeclasses that have kind '(Type -> Type) -> Constraint', e.g. 'Functor'
tcLaw1 ::
( Ctx f
, forall x. Eq x => Eq (f x)
, forall x. Show x => Show (f x)
) => (forall x. Gen x -> Gen (f x)) -> Laws
-- Typeclasses that have kind '(Type -> Type -> Type) -> Constraint', e.g. 'Bifunctor'
tcLaw2 ::
( Ctx f
, forall x y. (Eq x, Eq y) => Eq (f x y)
, forall x y. (Show x, Show y) => Show (f x y)
) => (forall x y. Gen x -> Gen y -> Gen (f x y)) -> Laws
The third and last part of hedgehog-classes
are the three convenience functions used to run your tests. They all return an IO Bool
, where True
is returned if all the tests pass, and False
otherwise. They are as following:
-- Test a single typeclasses' laws.
lawsCheck :: Laws -> IO Bool
-- Test multiple typeclass laws for a single type.
lawsCheckOne :: Gen a -> [Gen a -> Laws] -> IO Bool
-- Test mutliple typeclass laws for multiple types.
-- The argument is pairs of type names and their associated laws to test.
lawsCheckMany :: [(String, [Laws])] -> IO Bool
That is all there is to using hedgehog-classes
in your test suite. For usage examples, see the haddocks.
Distributing your own Laws
hedgehog-classes
also exports some functions which you may find useful for writing functions that allow users to test the laws of typeclasses you define in your own libraries, along with utilities for providing custom error messages. They can be found here.
Example error messages
Below is an example of an error message one might get from a failed test from hedgehog-classes
:
Similar libraries
There are a number of libraries that have similar goals to hedgehog-classes
:
Supported Typeclasses
base
- Alternative
- Applicative
- Arrow
- Bifoldable
- Bifunctor
- Bitraversable
- Bits/FiniteBits
- Category
- Contravariant
- Enum
- Eq
- Foldable
- Functor
- Generic
- Integral
- Monad
- MonadIO
- MonadPlus
- MonadZip
- Ord
- Semigroup
- Show
- ShowRead
- Storable
- Traversable
aeson
- ToJSON
- ToJSON/FromJSON
comonad
- Comonad
semirings
- Semiring
- Ring
primitive
- Prim
Some typeclasses can have additional laws, which are not part of their sufficient definition. A common example is commutativity of a monoid. In such cases where this is sensible, hedgehog-classes
provides functions such commutativeMonoidLaws
, commutativeSemigroupLaws
, etc. hedgehog-classes
also tests that foldl'
/foldr'
actually accumulate strictly. There are other such cases that are documented on Hackage.
Support will be added for the typeclasses from semigroupoids.
Support will be added for the Semiring
/Ring
typeclasses from semirings.
Building
Currently, you need GHC >= 8.5 to build this (because of -XQuantifiedConstraints
). Some CPP can be used to make this buildable with older GHCs, I just have not done so yet. I would gladly take a PR that does so, but only for GHC 8.2.2 and newer.
To use this library for testing, just add it to a test stanza of your cabal file.
To use this library to export your own Laws
functions which you wish to distribute, add it to the library stanza of your cabal file.
Improvements
There are a number of improvements that can be made to the API of hedgehog-classes
:
- Traversable needs better error messages, without exposing library internals.
- Arrow Laws 5/6/7 need names.
- Some laws could use better names, as some of them I had to make up.
- ixLaws can accidentally be extremely inefficient and I’m not sure how to fix that.
- The test suite is incomplete.
- There is no ‘bad’ test suite, for testing error messages.
- There could be spelling mistakes/grammatical errors/inconsistencies in the custom error messages.
You can help fix any of the above by opening an issue/PR! Thanks.
Changes
Changelog
hedgehog-classes
uses PVP Versioning.
The changelog is available on GitHub.
Unreleased
- Make
Hedgehog.Classes.Aeson
module empty when theaeson
flag is disabled. - Make
Hedgehog.Classes.Prim
module empty when theprimitive
flag is disabled.
0.2.5.3
- Correct bug in which
storablePeekByte
uses the wrong offset values - Update base upper bound. [4.12, 4.15) -> [4.12, 4.17)
- Update semirings upper bound. [0.2, 0.7) -> [0.2, 0.8)
- Update aeson upper bound. [0.9, 1.6) -> [0.9, 2.1)
0.2.5.2
- Update semirings upper bound. [0.2, 0.6) -> [0.2, 0.7)
0.2.5.1
- Bump upper bound on pretty-show from <1.10 to <1.11
0.2.5
- Add MUVector laws
- Update upper bounds on dependencies
0.2.4.1
- Fix error introduced by change of hedgehog’s internal API between hedgehog-1.0.1 and hedgehog-1.0.2.
- Re-add GHC 8.8.1 to cabal’s tested-with field.
0.2.4
- Semirings upper bound increased to 0.6. [0.2, 0.5) -> [0.2, 0.6)
- Add
primLaws
. - Remove GHC 8.8.1 from cabal’s tested-with field.
- Add documentation to
comonadLaws
.
0.2.3
- Semirings upper bound increased to 0.5. Lower bound not touched. [0.2, 0.4) -> [0.2, 0.5)
- Add
comonadLaws
.
0.2.2
- fix problem in storable set-get that caused attempt to index into 0-element malloc’d array
- Test suite now tests almost all laws sans arrow/category (thanks @ag-eitilt!)
- Correct tcName of
MonadPlus
. WasMonad
, now it’sMonadPlus
.
0.2.1
- fix problem where ordLaws failed for everything. there was some messed up logic used to check that transitivity held. Thanks very much to @ocharles for reporting this.
0.2.0.1
- improve reliability of hedgehog output filtering.
0.2
- switch to hedgehog-1.0
- add
binaryLaws
- relax cabal-version to 2.2
- use randomly generated, not hard-coded functions, in bifoldable tests
- significantly simplify pretty printing using
silently
package, and bad hack. - make several haddock improvements.
0.1.2
- add
semiringLaws
,ringLaws
,starLaws
- fix bug in
foldableLaws
that could cause implementations offoldMap
andfold
that evaluate in weird orders to pass (rather than fail).
0.1.1
- Initial (stable) hackage release.
0.0.0
- Initially created.