2016-06-16 2 views
1

Предположит, что мы имеем свойство QuickCheck подтверждающее, что экземпляр Functor удовлетворяет закон идентичности:Возможно ли сопоставить quickCheck над списком типов?

-- | A covariant functor. 
class Map m where 
    (<@>) :: (a -> b) -> m a -> m b 

data Proxy a = Proxy 

-- | The functor identity law. 
prop_mapIdentityLaw :: (Eq (m a), Show (m a), Map m) 
        => Proxy (m a) 
        -> m a 
        -> Bool 
prop_mapIdentityLaw _ x = (id <@> x) == id x 

Я хотел бы написать функцию, которая управляет этим свойством в отношении случаев, которые я создаю. Что-то примерно похожее на:

verify :: IO() 
verify = 
    mapM_ quickCheck [ prop_mapIdentityLaw t | t <- types ] 
    where 
    types = 
     [ Proxy :: Proxy (Id Int) ] 

Если бы я добавить еще один прокси-сервер в мой список типов (например Proxy :: Proxy (Option Int)), очевидно, что не будет работать, потому что Proxy имеет тип Proxy (Id Int).

Я использовал Haskell много лет назад, но я не знаком с некоторыми из новых расширений. Мне интересно, есть ли способ достичь этого с помощью RankNTypes и DataKinds или некоторой другой комбинации расширений.

+1

Это не относится к делу, но я предлагаю вам заменить результат 'Bool' на' Property', используя '===' вместо '=='. Таким образом, неудачи тестов покажут, что было на каждой стороне теста равенства, а не просто «ложь». – dfeuer

+0

Спасибо @dfeuer за подсказку! Я не знал о '===' операторе, и раньше было интересно, есть ли простой способ сделать его «Свойством». –

ответ

2

Вы должны упаковать соответствующие словари (свидетели случаев) в экзистенциальном типе, так, чтобы сделать «список типов» однородно:

-- Carries a Proxy with a bunch of dictionaries 
data ProxyA where 
    ProxyA :: (Eq (m a), Show (m a), Map m, Arbitrary (m a)) => Proxy (m a) -> ProxyA 

-- | The functor identity law. 
prop_mapIdentityLaw :: (Eq (m a), Show (m a), Map m) 
        => Proxy (m a) 
        -> m a 
        -> Bool 
prop_mapIdentityLaw _ x = (id <@> x) == id x 

verify :: IO() 
verify = forM_ types $ \ (ProxyA p) -> quickCheck $ prop_mapIdentityLaw p 
    where 
    types :: [ ProxyA ] 
    types = 
     [ ProxyA (Proxy :: Proxy (Id Int)) 
     , ProxyA (Proxy :: Proxy (Id Char)) 
     ] 

Обратите внимание, что, из-за того, как экзистенциальными виды работы, вы не может составить список частично приложений, как в [ prop_mapIdentityLaw p | ProxyA p <- types ], так как этот список не является однородным. Если вы добавите quickCheck сверху, то он станет однородным.

Смежные вопросы