我有一个环类型类,如下所示:
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功能有点丑陋和重复。原因是我的测试具有不同数量的参数。是否有更好的方法(即锅炉板代码较少的方法)来测试所有实例?
答案 0 :(得分:3)
我认为类型类函数是这里的方法。一种简单的方法是将另一个成员添加到类Ring
,例如ringTests :: (Eq a, Arbitrary a) => proxy a -> IO Result
。代理参数是必要的,以便调用者可以指定要测试的类型:ringTests (Proxy :: Proxy Matrix)
。
ringTests
可以有一个易于编写的默认实现;您可能需要ScopedTypeVariables
来获取范围内的类型变量。
您当然也可以将测试函数放在一个单独的类中,同样使用默认实现。然后你只需要编写instance RingTests Matrix where
等等,无需实现。