是否可以将quickCheck映射到类型列表?

时间:2016-06-16 07:21:42

标签: haskell

假设我们有一个quickcheck属性,用于验证Functor的实例是否满足身份法:

-- | 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,但我不熟悉一些较新的扩展。我想知道是否有办法通过RankNTypesDataKinds或其他一些扩展组合实现这一目标。

1 个答案:

答案 0 :(得分: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,那么它就会变得同质。