{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
module Hedgehog.Classes.Monad (monadLaws) where
import Control.Monad (ap)
import Hedgehog
import Hedgehog.Classes.Common
monadLaws ::
( Monad f
, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)
) => (forall x. Gen x -> Gen (f x)) -> Laws
monadLaws gen = Laws "Monad"
[ ("Left Identity", monadLeftIdentity gen)
, ("Right Identity", monadRightIdentity gen)
, ("Associativity", monadAssociativity gen)
, ("Return", monadReturn gen)
, ("Ap", monadAp gen)
]
type MonadProp f =
( Monad f
, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)
) => (forall x. Gen x -> Gen (f x)) -> Property
monadLeftIdentity :: forall f. MonadProp f
monadLeftIdentity _ = property $ do
k' :: LinearEquationM f <- forAll genLinearEquationM
a <- forAll $ genSmallInteger
let k = runLinearEquationM k'
let lhs = return a >>= k
let rhs = k a
let ctx = contextualise $ LawContext
{ lawContextLawName = "Left Identity", lawContextTcName = "Monad"
, lawContextLawBody = "return a >>= k" `congruency` "k a"
, lawContextReduced = reduced lhs rhs
, lawContextTcProp =
let showK = show k'
showA = show a
in lawWhere
[ "return a >>= k" `congruency` "k a, where"
, "k = " ++ showK
, "a = " ++ showA
]
}
heqCtx1 lhs rhs ctx
monadRightIdentity :: forall f. MonadProp f
monadRightIdentity fgen = property $ do
m <- forAll $ fgen genSmallInteger
let lhs = m >>= return
let rhs = m
let ctx = contextualise $ LawContext
{ lawContextLawName = "Right Identity", lawContextTcName = "Monad"
, lawContextLawBody = "m >>= return" `congruency` "m"
, lawContextReduced = reduced lhs rhs
, lawContextTcProp =
let showM = show m
in lawWhere
[ "m >>= return" `congruency` "m, where"
, "m = " ++ showM
]
}
heqCtx1 lhs rhs ctx
monadAssociativity :: forall f. MonadProp f
monadAssociativity fgen = property $ do
m <- forAll $ fgen genSmallInteger
k' :: LinearEquationM f <- forAll genLinearEquationM
h' :: LinearEquationM f <- forAll genLinearEquationM
let k = runLinearEquationM k'
h = runLinearEquationM h'
let lhs = m >>= (\x -> k x >>= h)
let rhs = (m >>= k) >>= h
let ctx = contextualise $ LawContext
{ lawContextLawName = "Associativity", lawContextTcName = "Monad"
, lawContextLawBody = "m >>= (\\x -> k x >>= h)" `congruency` "(m >>= k) >>= h"
, lawContextReduced = reduced lhs rhs
, lawContextTcProp =
let showM = show m
showK = show k'
showH = show h'
in lawWhere
[ "m >>= (\\x -> k x >>= h)" `congruency` "(m >>= k) >>= h, where"
, "m = " ++ showM
, "k = " ++ showK
, "h = " ++ showH
]
}
heqCtx1 lhs rhs ctx
monadReturn :: forall f. MonadProp f
monadReturn _ = property $ do
x <- forAll genSmallInteger
let lhs = return x
let rhs = pure x :: f Integer
let ctx = contextualise $ LawContext
{ lawContextLawName = "Return", lawContextTcName = "Monad"
, lawContextLawBody = "return" `congruency` "pure"
, lawContextReduced = reduced lhs rhs
, lawContextTcProp =
let showX = show x
in lawWhere
[ "return x" `congruency` "pure x, where"
, "x = " ++ showX
]
}
heqCtx1 lhs rhs ctx
monadAp :: forall f. MonadProp f
monadAp _ = property $ do
f' :: f QuadraticEquation <- forAll $ pure <$> genQuadraticEquation
x :: f Integer <- forAll $ pure <$> genSmallInteger
let f = fmap runQuadraticEquation f'
let lhs = ap f x
let rhs = f <*> x
let ctx = contextualise $ LawContext
{ lawContextLawName = "Ap", lawContextTcName = "Monad"
, lawContextLawBody = "ap f" `congruency` "f <*>"
, lawContextReduced = reduced lhs rhs
, lawContextTcProp =
let showX = show x
showF = show f'
in lawWhere
[ "ap f x" `congruency` "f <*> x, where"
, "f = " ++ showF
, "x = " ++ showX
]
}
heqCtx1 lhs rhs ctx