2014-10-20 87 views
4

我有一個環型類看起來像這樣可變數目:測試類型類與快速檢查,參數

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 ... 

爲了測試這種情況下,我有以下快速檢查測試:

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

這看起來是一個類型家庭的工作。我現在沒有時間進行實驗,但也許有人會這樣做。 – chi 2014-10-20 14:59:07

+0

我不認爲你可以自動做到這一點。此外,我認爲這甚至不可取。如果您的模塊的用戶將您的測試函數導入到她定義自定義實例的模塊中,該函數是否應該自動測試該實例? – DanielM 2014-10-20 17:46:37

回答

3

我認爲類型類函數是去這裏的方法。一種簡單的方法是添加另一個成員Ring,如ringTests :: (Eq a, Arbitrary a) => proxy a -> IO Result。代理參數將是必要的,以便呼叫者可以指定要測試的類型:ringTests (Proxy :: Proxy Matrix)

ringTests可以有一個應該很容易編寫的默認實現;您可能需要ScopedTypeVariables來獲取範圍內的類型變量。

你當然也可以把測試函數放在一個單獨的類中,再次使用默認實現。那麼你只需要編寫instance RingTests Matrix where等等,不需要實現。

+0

非常感謝!這正是我所期待的。 – Arno 2014-10-21 07:39:27