使用Quickcheck测试类型类,参数可变

时间:2014-10-20 14:53:58

标签: haskell typeclass quickcheck

我有一个环类型类,如下所示:

class Ring a where
  addId  :: a
  addInverse :: a -> a
  mulId  :: a
  add :: a -> a -> a
  mul :: a -> a -> a

对于这门课,我有几个例子,例如

instance Ring Matrix where ...
instance Ring Integer where ...
instance Ring Modulo where ...

为了测试这些实例,我有以下quickcheck测试:

prop_AddId :: (Ring a, Eq a, Arbitrary a) => a -> Bool
prop_AddInv :: (Ring a, Eq a, Arbitrary a) => a -> Bool
prop_MulId :: (Ring a, Eq a, Arbitrary a) => a -> Bool
prop_AddCommutative :: (Ring a, Eq a, Arbitrary a) => a -> a -> Bool
prop_AddAssociative :: (Ring a, Eq a, Arbitrary a) => a -> a -> a -> Bool
prop_MulAssociative :: (Ring a, Eq a, Arbitrary a) => a -> a -> a -> Bool
prop_Distributive :: (Ring a, Eq a, Arbitrary a) => a -> a -> a -> Bool

我不确定如何为我的所有类实例运行这些测试用例。我找到了解决方案here 这导致以下结果:

forallRings :: (forall a. (Ring a, Arbitrary a, Eq a) => a -> Bool) -> [IO ()]
forallRings x =
    [ quickCheck (x :: Matrix -> Bool)
    , quickCheck (x :: Integer -> Bool)
    , quickCheck (x :: Modulo -> Bool)
    ]
forallRings2 :: (forall a. (Ring a, Arbitrary a, Eq a) => a -> a -> Bool) -> [IO ()]
forallRings2 x =
    [ quickCheck (x :: Matrix -> Matrix -> Bool)
    , quickCheck (x :: Integer -> Integer -> Bool)
    , quickCheck (x :: Modulo -> Modulo -> Bool)
    ]
forallRings3 :: (forall a. (Ring a, Arbitrary a, Eq a) => a -> a -> a -> Bool) -> [IO ()]
forallRings3 x =
    [ quickCheck (x :: Matrix -> Matrix -> Matrix -> Bool)
    , quickCheck (x :: Integer -> Integer -> Integer -> Bool)
    , quickCheck (x :: Modulo -> Modulo -> Modulo -> Bool)
    ]

ringTests :: IO ()
ringTests = sequence_ $
            forallRings propAddId
            ++ forallRings prop_AddInv
            ++ forallRings prop_MulId
            ++ forallRings2 prop_AddCommutative
            ++ forallRings3 prop_AddAssociative
            ++ forallRings3 prop_MulAssociative
            ++ forallRings3 prop_Distributive

我对这个解决方案有点不满意。我发现forAllRingsX功能有点丑陋和重复。原因是我的测试具有不同数量的参数。是否有更好的方法(即锅炉板代码较少的方法)来测试所有实例?

1 个答案:

答案 0 :(得分:3)

我认为类型类函数是这里的方法。一种简单的方法是将另一个成员添加到类Ring,例如ringTests :: (Eq a, Arbitrary a) => proxy a -> IO Result。代理参数是必要的,以便调用者可以指定要测试的类型:ringTests (Proxy :: Proxy Matrix)

ringTests可以有一个易于编写的默认实现;您可能需要ScopedTypeVariables来获取范围内的类型变量。

您当然也可以将测试函数放在一个单独的类中,同样使用默认实现。然后你只需要编写instance RingTests Matrix where等等,无需实现。