{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
module Hedgehog.Classes.Functor (functorLaws) where
import Hedgehog
import Hedgehog.Classes.Common
functorLaws ::
( Functor f
, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)
) => (forall x. Gen x -> Gen (f x)) -> Laws
functorLaws gen = Laws "Functor"
[ ("Identity", functorIdentity gen)
, ("Composition", functorComposition gen)
, ("Const", functorConst gen)
]
functorIdentity ::
( Functor f
, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)
) => (forall x. Gen x -> Gen (f x)) -> Property
functorIdentity fgen = property $ do
a <- forAll $ fgen genSmallInteger
let lhs = fmap id a
let rhs = id a
let ctx = contextualise $ LawContext
{ lawContextLawName = "Identity", lawContextTcName = "Functor"
, lawContextLawBody = "fmap id" `congruency` "id"
, lawContextTcProp =
let showA = show a
in lawWhere
[ "fmap id a" `congruency` "id a, where"
, "a = " ++ showA
]
, lawContextReduced = reduced lhs rhs
}
heqCtx lhs rhs ctx
functorComposition ::
( Functor f
, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)
) => (forall x. Gen x -> Gen (f x)) -> Property
functorComposition fgen = property $ do
a <- forAll $ fgen genSmallInteger
let f = func2; g = func1
let lhs = fmap f (fmap g a)
let rhs = fmap (f . g) a
let ctx = contextualise $ LawContext
{ lawContextLawName = "Composition", lawContextTcName = "Functor"
, lawContextLawBody = "fmap f . fmap g" `congruency` "fmap (f . g)"
, lawContextTcProp =
let showA = show a
showF = "\\(a,b) -> (odd a, if even a then Left (compare a b) else Right (b + 2)"
showG = "\\i -> (div (i + 5) 3, i * i - 2 * i + 1)"
in lawWhere
[ "fmap f . fmap g $ a" `congruency` "fmap (f . g) a, where"
, "f = " ++ showF
, "g = " ++ showG
, "a = " ++ showA
]
, lawContextReduced = reduced lhs rhs
}
heqCtx lhs rhs ctx
functorConst ::
( Functor f
, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)
) => (forall x. Gen x -> Gen (f x)) -> Property
functorConst fgen = property $ do
a <- forAll $ fgen genSmallInteger
let x = 'X'
let lhs = fmap (const x) a
let rhs = x <$ a
let ctx = contextualise $ LawContext
{ lawContextLawName = "Const", lawContextTcName = "Functor"
, lawContextLawBody = "fmap (const x)" `congruency` "x <$"
, lawContextTcProp =
let showA = show a
showX = show x
in lawWhere
[ "fmap (const x) a" `congruency` "x <$ a, where"
, "x = " ++ showX
, "a = " ++ showA
]
, lawContextReduced = reduced lhs rhs
}
heqCtx lhs rhs ctx