2014-10-20 3 views
4

у меня есть класс типа кольца, которое выглядит следующим образом:классов типов тестирования с 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 

Я уверен, как запустить эти testcases для всех моих экземпляров класса. Я нашел решение 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

+0

Я не думаю, что вы можете сделать это автоматически. Более того, я думаю, что это даже не было бы желательно. Если пользователь вашего модуля должен был импортировать вашу тестовую функцию в модуль, где она определила собственный экземпляр, должна ли функция автоматически проверить этот экземпляр? – DanielM

ответ

3

Я думаю, что функция typeclass - это путь сюда. Один простой способ - добавить другого члена в класс Ring, например ringTests :: (Eq a, Arbitrary a) => proxy a -> IO Result. Аргумент прокси-сервера будет необходим, чтобы вызывающий мог указать тип теста: ringTests (Proxy :: Proxy Matrix).

ringTests может иметь реализацию по умолчанию, которую следует легко написать; вам может понадобиться ScopedTypeVariables, чтобы получить переменную типа в области.

Вы также можете поместить тестовую функцию в отдельный класс, опять же с реализацией по умолчанию. Тогда вам просто нужно написать instance RingTests Matrix where и т. Д., Без необходимости реализации.

+0

Спасибо вам большое! Это именно то, что я искал. – Arno