简体   繁体   中英

QuickCheck Semigroup over a function

I have this method to test associativity for a Semigroup

semigroupdAssoc :: (Eq m, Semigroup m) => m -> m -> m -> Bool
semigroupdAssoc a b c = a <> (b <> c) == (a <> b) <> c

Given the following type

newtype Combine a b =
    Combine { unCombine :: a -> b }

My Semigroup implementation i

instance Semigroup b => Semigroup (Combine a b) where
    (<>) (Combine u) (Combine u') = Combine (u <> u')

I am stuck on how should I write a quick test,

Let's say I want to test for String,

type CombineAssoc = Combine String String -> Combine String String -> Combine String String -> Bool

And the test will be

    quickCheck (semigroupdAssoc :: CombineAssoc)

I know I have to write a Arbitrary implementation for Combine but I can't figure out how.

Figured out solution, but I don't understand it.

implementation of Arbitrary for Combine looks like this:

instance (CoArbitrary a, Arbitrary b) => Arbitrary  (Combine a b) where
    arbitrary = do
        Combine <$> arbitrary

need to implement Show also (not really great)

instance Show (Combine a b) where
    show (Combine _) = "unCombine"

Update the assoc function for this data type

combineSemigroupAssoc :: (Eq b, Semigroup b) => a -> Combine a b -> Combine a b -> Combine a b -> Bool
combineSemigroupAssoc x a b c = unCombine (a <> (b <> c)) x == unCombine ((a <> b) <> c) x

Implement the property that needs testing

genString :: Gen String
genString = arbitrary

prop_combineSemigroupAssoc :: Property
prop_combineSemigroupAssoc = forAll genString (combineSemigroupAssoc :: CombineAssoc)

and finally run quickCheck

    quickCheck prop_combineSemigroupAssoc

Thinks I still need help with

  • Can you please explain how the Arbitrary implementation works now (docs for CoArbitrary are not very clear to me)?
  • Is there a better way to implement Show for Combine, like see the actual call param?

I recommend writing your property in terms of Fun :

type SS = Fun String String -- just to shorten things a bit

prop_CombineAssoc :: SS -> SS -> SS -> String -> Bool
prop_CombineAssoc f_ g_ h_ s =
    unCombine (f <> (g <> h)) s == unCombine ((f <> g) <> h) s
    where
    [f, g, h] = map (Combine . applyFun) [f_, g_, h_]

The above property passes, but for fun, here's what it looks like for a failure. To get this output, I changed (f <> (g <> h)) to just f .

> quickCheck prop_CombineAssoc
*** Failed! Falsified (after 3 tests and 16 shrinks):    
{_->""}
{_->""}
{_->"a"}
""

As you can see, the Show instance for Fun is more informative than simply returning a constant String , as your instance for Combine does.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM