HORIZON HASKELLDocslts/ghc-10.0.x8c2f8e82026-10-19Search names, modules, packages, or :: a typeCtrl K

GHC 10.0.0.20260917 · lts/ghc-10.0.x · 8c2f8e8 · 2026-10-19

Modulehedgehog-classes-0.2.5.4Haskell2010

Hedgehog.Classes

This library provides sets of properties that should hold for common typeclasses.

Note: functions that test laws of a subclass never test the laws of a superclass. For example, commutativeSemigroupLaws never tests the laws provided by semigroupLaws.

  • 3 types
  • 60 values

Running

3 declarations
valuelawsCheck
  1. :: Laws

    The Laws you would like to check.

  2. -> IO Bool

    True if your tests pass, False otherwise.

#

A convenience function for testing the properties of a typeclass. For example, in GHCi:

Example2 expressions
genOrdering :: Gen Ordering; genOrdering = frequency [(1,pure EQ),(1,pure LT),(1,pure GT)]lawsCheck (monoidLaws genOrdering)Monoid: Left Identity    ✓ <interactive> passed 100 tests.Monoid: Right Identity    ✓ <interactive> passed 100 tests.Monoid: Associativity    ✓ <interactive> passed 100 tests.Monoid: Concatenation    ✓ <interactive> passed 100 tests.True
valuelawsCheckOne
  1. :: Gen a

    The generator for your type.

  2. -> [Gen a -> Laws]

    Functions that take a generator and output Laws.

  3. -> IO Bool

    True if your tests pass. False otherwise.

#

A convenience function for testing many typeclass instances of a single type.

Example1 expression
lawsCheckOne (word8 constantBounded) [jsonLaws, showReadLaws]ToJSON/FromJSON: Partial Isomorphism    ✓ <interactive> passed 100 tests.ToJSON/FromJSON: Encoding equals value    ✓ <interactive> passed 100 tests.Show/Read: Partial Isomorphism: show/read    ✓ <interactive> passed 100 tests.Show/Read: Partial Isomorphism: show/read with initial space    ✓ <interactive> passed 100 tests.Show/Read: Partial Isomorphism: showsPrec/readsPrec    ✓ <interactive> passed 100 tests.Show/Read: Partial Isomorphism: showList/readList    ✓ <interactive> passed 100 tests.Show/Read: Partial Isomorphism: showListWith shows/readListDefault    ✓ <interactive> passed 100 tests.True
valuelawsCheckMany
  1. :: [(String, [Laws])]

    Pairs of type names and their associated laws to test.

  2. -> IO Bool

    True if your tests pass. False otherwise.

#

A convenience function for checking many typeclass instances of multiple types.

import Control.Applicative (liftA2)

import Data.Map (Map)
import Data.Set (Set)

import qualified Data.List as List
import qualified Data.Set as Set
import qualified Data.Map as Map

import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

import Hedgehog (Gen)
import Hedgehog.Classes

-- Generate a small Set Int
genSet :: Gen (Set Int)
genSet = Set.fromList <$> (Gen.list (Range.linear 2 10) (Gen.int Range.constantBounded))

-- Generate a small Map String Int
genMap :: Gen (Map String Int)
genMap = Map.fromList <$> (liftA2 List.zip genStrings genInts)
  where
    rng = Range.linear 2 6
    genStrings = Gen.list rng (Gen.string rng Gen.lower)
    genInts = Gen.list rng (Gen.int Range.constantBounded)

commonLaws :: (Eq a, Monoid a, Show a) => Gen a -> [Laws]
commonLaws p = [eqLaws p, monoidLaws p]

tests :: [(String, [Laws])]
tests =
  [ ("Set Int", commonLaws genSet)
  , ("Map String Int", commonLaws genMap)
  ]

Now, in GHCi:

Example1 expression
lawsCheckMany tests
Testing properties for common typeclasses...

-------------
-- Set Int --
-------------

Eq: Transitive   ✓ interactive passed 100 tests.
Eq: Symmetric   ✓ interactive passed 100 tests.
Eq: Reflexive   ✓ interactive passed 100 tests.
Eq: Negation   ✓ interactive passed 100 tests.
Monoid: Left Identity   ✓ interactive passed 100 tests.
Monoid: Right Identity   ✓ interactive passed 100 tests.
Monoid: Associativity   ✓ interactive passed 100 tests.
Monoid: Concatenation   ✓ interactive passed 100 tests.

--------------------
-- Map String Int --
--------------------

Eq: Transitive   ✓ interactive passed 100 tests.
Eq: Symmetric   ✓ interactive passed 100 tests.
Eq: Reflexive   ✓ interactive passed 100 tests.
Eq: Negation   ✓ interactive passed 100 tests.
Monoid: Left Identity   ✓ interactive passed 100 tests.
Monoid: Right Identity   ✓ interactive passed 100 tests.
Monoid: Associativity   ✓ interactive passed 100 tests.
Monoid: Concatenation   ✓ interactive passed 100 tests.

All tests succeeded
True

Properties

0 declarations

Ground types

valuebitsLaws :: (FiniteBits a, Show a) => Gen a -> Laws
#

Tests the following Bits laws:

Conjunction Idempotence

n .&. n

≡

n

Disjunction Idempotence

n .|. n

≡

n

Double Complement

complement . complement

≡

id

Set Bit

setBit n i ≡ n .|. bit i

Clear Bit

clearBit n i

≡

n .&. complement (bit i)

Complement Bit

complement n i

≡

xor n (bit i)

Clear Zero

clearBit zeroBits i

≡

zeroBits

Set Zero

setBit zeroBits i

≡

zeroBits

Test Zero

testBit zeroBits i

≡

False

Pop Zero

popCount zeroBits

≡

0

Count Leading Zeros of Zero

countLeadingZeros zeroBits

≡

finiteBitSize (undefined :: a)

Count Trailing Zeros of Zero

countTrailingZeros zeroBits

≡

finiteBitSize (undefined :: a)

valueprimLaws :: (Prim a, Eq a, Show a) => Gen a -> Laws
#

Tests the following Prim laws:

ByteArray Set-Get

primitive_ (writeByteArray# ba# ix# x) *> primitive (readByteArray# ba# ix#)

≡

pure x

ByteArray Get-Set

primitive (readByteArray# ba# ix#) >>= x -> primitive_ (writeByteArray# ba# ix# x)

≡

pure ()

ByteArray Set-Set

primitive_ (writeByteArray# ba# ix# x) *> primitive_ (writeByteArray# ba# ix# x)

≡

primitive_ (writeByteArray# ba# ix# x)

ByteArray Set Range

The behavior of

setByteArray#

matches the default implementation

ByteArray List Conversion Roundtrips

Turning a list into a

PrimArray

and back gives you the same list

Addr Set-Get

primitive_ (writeOffAddr# addr# ix# x) *> primitive (readOffAddr# addr# ix#)

≡

pure x

Addr Get-Set

primitive (readOffAddr# addr# ix#) >>= x -> primitive_ (writeOffAddr# addr# ix# x)

≡

pure ()

Addr Set-Set

primitive_ (writeOffAddr# addr# ix# x) *> primitive_ (writeOffAddr# addr# ix# x)

≡

primitive_ (writeOffAddr# addr# ix# x)

Addr Set Range

The behavior of

setOffAddr#

matches the default implementation

Addr List Conversion Roundtrips

Mallocing a list and then reconstructing it gives you the same list

valuesemiringLaws :: (Semiring a, Eq a, Show a) => Gen a -> Laws
#

Tests the following Semiring laws:

Additive Left Identity

zero + x

≡

x

Additive Right Identity

x + zero

≡

x

Additive Associativity

x + (y + z)

≡

(x + y) + z

Additive Commutativity

x + y

≡

y + x

Multiplicative Left Identity

one * x

≡

x

Multiplicative Right Identity

x * one

≡

x

Multiplicative Associativity

x * (y * z)

≡

(x * y) * z

Multiplicatiion Left-Distributes Over Addtion

x * (y + z)

≡

(x * y) + (x * z)

Multiplication Right-Distibutes Over Addition

(y + z) * x

≡

(y * x) + (z * x)

Multiplicative Left Annihilation

zero * x

≡

zero

Multiplicative Right Annihilation

x * zero

≡

zero

valueshowReadLaws :: (Eq a, Read a, Show a) => Gen a -> Laws
#

Tests the following Show / Read laws:

Partial Isomorphism: show/read

readMaybe . show

≡

Just

Partial Isomorphism: show/read with initial space

readMaybe . (" " ++) . show

≡

Just

Partial Isomorphism: showsPrec/readPrec

(a,"") elem readsPrec p (showsPrec p a "")

≡

True

Partial Isomorphism: showList/readList

(as,"") elem readList (showList as "")

≡

True

Partial Isomorphism: showListWith shows/readListDefault

(as,"") elem readListDefault (showListWith shows as "")

≡

True

valuestorableLaws :: (Eq a, Show a, Storable a) => Gen a -> Laws
#

Tests the following Storable laws:

Set-Get

pokeElemOff ptr ix a >> peekElemOff ptr ix

≡

pure a

Get-Set

peekElemOff ptr ix >>= pokeElemOff ptr ix

≡

pure ()

(Putting back what you got out has no effect)

List Conversion Roundtrips

Mallocing a list and then reconstructing it gives you the same list

PeekElemOff/Peek

peekElemOff a i

≡

peek (plusPtr a (i * sizeOf undefined))

PokeElemOff/Poke

pokeElemOff a i x

≡

poke (plusPtr a (i * sizeOf undefined)) x

PeekByteOff/Peek

peekByteOff a i

≡

peek (plusPtr a i)

PokeByteOff/Peek

pokeByteOff a i x

≡

poke (plusPtr a i) x

Unary type constructors

valuecomonadLaws
  1. :: (Comonad f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x))
  2. => forall x. Gen x -> Gen (f x)
  3. -> Laws
#

Tests the following Comonad laws:

Extend/Extract Identity

extend extract ≡ id

Extract/Extend

extract . extend f ≡ f

Extend/Extend

extend f . extend g ≡ extend (f . extend g)

Extract Right Identity

f =>= extract ≡ f

Extract Left Identity

extract =>= f ≡ f

Cokleisli Associativity

(f =>= g) =>= h ≡ f =>= (g =>= h)

Extract/Duplicate Identity

extract . duplicate ≡ id

Fmap Extract/Duplicate Identity

fmap extract . duplicate ≡ id

Double Duplication

duplicate . duplicate ≡ fmap duplicate . duplicate

Extend/Fmap . Duplicate Identity

extend f ≡ fmap f . duplicate

Duplicate/Extend id Identity

duplicate ≡ extend id

Fmap/Extend Extract

fmap f ≡ extend (f . extract)

Fmap/LiftW Isomorphism

fmap ≡ liftW

valuefoldableLaws
  1. :: (Foldable f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x))
  2. => forall x. Gen x -> Gen (f x)
  3. -> Laws
#

Tests the following Foldable laws:

Fold

fold ≡ foldMap id

FoldMap

foldMap f ≡ foldr (mappend . f) mempty

Foldr

foldr f z t ≡ appEndo (foldMap (Endo . f) t) z

Foldr'

foldr' f z0 t ≡ foldl f' id t z0, where f' k x z = k $! f x z

Foldl

foldl f z t ≡ appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z

Foldl'

foldl' f z0 xs ≡ foldr f' id xs z0, where f' x k z = k $! f z x

Foldl1

foldl1 f t ≡ let (x:xs) = toList t in foldl f x xs

Foldr1

foldr1 f t ≡ let (xs,x)

=

unsnoc (toList t) in foldr f x xs

ToList

toList ≡ foldr (:) []

Null

null ≡ foldr (const (const False)) True

Length

length ≡ getSum . foldMap (const (Sum 1))

This additionally tests that the user's implementations of foldr' and foldl' are strict in their accumulators.

valuetraversableLaws
  1. :: (Traversable f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x))
  2. => forall x. Gen x -> Gen (f x)
  3. -> Laws
#

Tests the following Traversable laws:

Naturality

t . traverse f

≡

traverse (t . f), for every applicative transformation t

Identity

traverse Identity

≡

Identity

Composition

traverse (Compose . fmap g . f)

≡

Compose . fmap (traverse g) . traverse f

SequenceA Naturality

t . sequenceA

≡

sequenceA . fmap t, for every applicative transformation t

SequenceA Identity

sequenceA . fmap Identity

≡

Identity

SequenceA Composition

sequenceA . fmap Compose

≡

Compose . fmap sequenceA . sequenceA

FoldMap

foldMap

≡

foldMapDefault

Fmap

fmap

≡

fmapDefault

Binary type constructors

valuearrowLaws
  1. :: (Arrow f, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y))
  2. => forall x y. Gen x -> Gen y -> Gen (f x y)
  3. -> Laws
#

Tests the following Arrow laws:

Arr Identity

arr id

≡

id

Arr Composition

arr (f >>> g)

≡

arr f >>> arr g

Arr-First inverse

first (arr f)

≡

arr (first f)

First Composition

first (f >>> g)

≡

first f >>> first g

Arrow Law 5

first f >>> arr fst

≡

arr fst >>> f

Arrow Law 6

first f >>> arr (id *** g)

≡

arr (id *** g) >>> first f

Arrow Law 7

first (first f) >>> arr assoc

≡

arr assoc >>> first f, where assoc ((a,b),c) = (a,(b,c))

valuebitraversableLaws
  1. :: (Bitraversable f, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y))
  2. => forall x y. Gen x -> Gen y -> Gen (f x y)
  3. -> Laws
#

Tests the following Bitraversable laws:

Naturality

bitraverse (t . f) (t . g)

≡

t . bitraverse f g, for every applicative transformation t

Identity

bitraverse Identity Identity

≡

Identity

Composition

Compose . fmap (bitraverse g1 g2) . bitraverse f1 f2

≡

bitraverse (Compose . fmap g1 . f1) (Compose . fmap g2 . f2)

Defining your own Laws

4 declarations

Hedgehog equality tests sans source information

14 declarations