packages feed

free-algebras-0.0.8.0: test/Test/Data/Algebra/Free.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell     #-}
{-# LANGUAGE TypeApplications    #-}

module Test.Data.Algebra.Free
    ( tests
    ) where

import           Control.Monad (join)
import           Data.List.NonEmpty (NonEmpty (..))
import           Data.Foldable (fold)
import           Data.Monoid (Sum (..))
import           Hedgehog (Property, Gen, property, (===))
import qualified Hedgehog as H
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

import           Data.Algebra.Free
                    ( AlgebraType
                    , AlgebraType0
                    , FreeAlgebra (..)
                    , foldFree
                    , unFoldMapFree
                    , natFree
                    , fmapFree
                    , joinFree
                    , bindFree
                    )

natFree_property
    :: ( FreeAlgebra  f
       , AlgebraType0 f a
       , AlgebraType  f (f a)
       , Eq (f a)
       , Show (f a)
       )
    => Gen (f a) -> Property
natFree_property gen = property $ do
    fa <- H.forAll gen
    natFree fa === fa

prop_natFree_list :: Property
prop_natFree_list = natFree_property
    $ Gen.list (Range.linear 0 100) Gen.alpha

prop_nafF_nonempty :: Property
prop_nafF_nonempty = natFree_property
    $ Gen.nonEmpty (Range.linear 0 100) Gen.alpha

-- |
-- Check that @'foldFree' is @'fold'@ for @f@ which are @'Foldable'@ and @a@ which
-- are @'Monoid' a.
foldFree_property
    :: ( FreeAlgebra  f
       , AlgebraType0 f a
       , AlgebraType  f a
       , Monoid a   -- fold brings this constraint, @'foldFree'@ is free of it!
       , Foldable f
       , Eq a
       , Eq (f a)
       , Show a
       , Show (f a)
       )
    => Gen (f a)
    -> Property
foldFree_property gen = property $ do
    fa <- H.forAll gen
    foldFree fa === fold fa

prop_foldFree_list :: Property
prop_foldFree_list = foldFree_property 
    $ (Gen.list $ Range.linear 0 100)
        (Sum <$> Gen.word32 (Range.linear 0 1024))

prop_foldFree_nonempty :: Property
prop_foldFree_nonempty = foldFree_property
    $ (Gen.nonEmpty $ Range.linear 0 100)
        (Sum <$> Gen.word32 (Range.linear 0 1024))

-- |
-- @'fmapFoldFree'@ is inverse of @'unFoldMapFree'@
foldMapFree_property
    :: forall f d a .
       ( FreeAlgebra  f
       , AlgebraType0 f d
       , AlgebraType0 f a
       , AlgebraType  f d
       , Show (f a)
       , Show a
       , Show d
       , Eq d
       )
    => Gen (f a)
    -> Gen a
    -> (f a -> d)
    -> (a -> d)
    -> Property
foldMapFree_property gen_fa gen fad ad = property $ do
    fa <- H.forAll gen_fa
    a  <- H.forAll gen
    unFoldMapFree (foldMapFree @f ad) a === ad a
    foldMapFree (unFoldMapFree @f fad) fa === fad fa

prop_foldMapFree_list :: Property
prop_foldMapFree_list
    = foldMapFree_property @[] @(Sum Int) @Int
        ((Gen.list $ Range.linear 0 100)
            (Gen.integral $ Range.linear 0 1024))
        (Gen.integral @_ @Int $ Range.linear 0 1024)
        (Sum . sum)
        Sum

prop_foldMapFree_nonempty :: Property
prop_foldMapFree_nonempty
    = foldMapFree_property @NonEmpty @(Sum Int) @Int
        ((Gen.nonEmpty $ Range.linear 0 100)
            (Gen.integral @_ @Int $ Range.linear 0 1024))
        (Gen.integral @_ @Int $ Range.linear 0 1024)
        (Sum . sum)
        Sum

-- |
-- @'fmapFree'@ should aggree with @'fmap'@ for types which satisfy @'Functor'@
-- constraint.
fmapFree_property
    :: forall f a b .
       ( FreeAlgebra  f
       , AlgebraType0 f a
       , AlgebraType0 f b
       , Functor f
       , Show (f a)
       , Eq (f a)
       , Show (f b)
       , Eq (f b)
       )
    => Gen (f a)
    -> (a -> b)
    -> Property
fmapFree_property gen f = property $ do
    fa <- H.forAll gen
    fmapFree f fa === fmap f fa

prop_fmapFree_list :: Property
prop_fmapFree_list = fmapFree_property @[] @Integer @Integer
    ((Gen.list $ Range.linear 0 100)
        (Gen.integral $ Range.linear 0 1024))
    (\x -> x^(2::Int) + 2 * x + 1)

prop_fmapFree_nonempty :: Property
prop_fmapFree_nonempty = fmapFree_property
    ((Gen.nonEmpty $ Range.linear 0 100)
        (Gen.integral @_ @Int $ Range.linear 0 1024))
    (\x -> x^(2::Int) + 2 * x + 1)

-- |
-- @'joinFree'@ should be equal to @'join'@ for monads.
joinFree_property
    :: ( FreeAlgebra  m
       , AlgebraType0 m a
       , AlgebraType0 m (m a)
       , AlgebraType  m (m a)
       , Monad m
       , Show (m (m a))
       , Eq (m (m a))
       , Show (m a)
       , Eq (m a)
       )
    => Gen (m (m a))
    -> Property
joinFree_property gen = property $ do
    mma <- H.forAll gen
    joinFree mma === join mma

prop_joinFree_list :: Property
prop_joinFree_list =
    let gen = Gen.list (Range.linear 0 100)
                (Gen.list (Range.linear 0 10) Gen.alpha)
    in joinFree_property gen

prop_joinFree_nonempty :: Property
prop_joinFree_nonempty =
    let gen = Gen.nonEmpty (Range.linear 0 100)
                (Gen.nonEmpty (Range.linear 0 10) Gen.alpha)
    in joinFree_property gen

-- |
-- @'bindFree'@ should be equal to @'>>='@ for monads.
bindFree_property
    :: ( FreeAlgebra  m
       , AlgebraType0 m a
       , AlgebraType0 m b
       , AlgebraType0 m (m b)
       , AlgebraType  m (m a)
       , AlgebraType  m (m b)
       , AlgebraType  m (m (m b))
       , Monad m
       , Show (m a)
       , Eq (m a)
       , Show (m b)
       , Eq (m b)
       )
    => Gen (m a)
    -> (a -> m b)
    -> Property
bindFree_property gen f = property $ do
    ma <- H.forAll gen
    bindFree ma f === (ma >>= f)

prop_bindFree_list :: Property
prop_bindFree_list =
    let gen = Gen.list
            (Range.linear 0 10)
            (Gen.integral @_ @Int $ Range.linear 0 1024)
    in bindFree_property gen (\x -> [x^(2 :: Int), 2 * x, 1])

prop_bindFree_nonempty :: Property
prop_bindFree_nonempty =
    let gen = Gen.nonEmpty
            (Range.linear 0 10)
            (Gen.integral @_ @Int $ Range.linear 0 1024)
    in bindFree_property gen (\x -> x^(2 :: Int) :| [2 * x, 1])

tests :: IO Bool
tests = H.checkParallel $$(H.discover)