Reputation: 118
Suppose we have a quickcheck property verifying that an instance of Functor
satisfies the identity law:
-- | 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
I would like to write a function that runs this property against instances I create. Something roughly akin to:
verify :: IO ()
verify =
mapM_ quickCheck [ prop_mapIdentityLaw t | t <- types ]
where
types =
[ Proxy :: Proxy (Id Int) ]
If I were to add another Proxy to my list of types (e.g. Proxy :: Proxy (Option Int)
), obviously that will not work, because Proxy
has type Proxy (Id Int)
.
I used Haskell a lot a few years ago, but I'm unfamiliar with some of the newer extensions. I'm wondering if there is a way to achieve this with RankNTypes
and DataKinds
or some other combination of extensions.
Upvotes: 1
Views: 105
Reputation: 116139
You need to pack the relevant dictionaries (witnesses of the instances) in an existential type, so to make the "type list" homogeneous:
-- 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))
]
Note that, because of how existential types work, you can't make a list of the partially applications as in [ prop_mapIdentityLaw p | ProxyA p <- types ]
since this list is not homogeneous. If you add quickCheck
on top, then it becomes homogeneous.
Upvotes: 2