我使用hspec和QuickCheck验证Functor实例的仿函数法则。我有功能
functorIdentity :: (Functor f, Eq (f a)) => f a -> Bool
和
functorComposition :: (Functor f, Eq (f c)) => (Fun a b) -> (Fun b c) -> f a -> Bool
然后我使用这样的代码块测试这两个:
testListFunctorness :: IO ()
testListFunctorness =
hspec $ do
describe "list" $ do
it "should obey functor identity" $ do
property (functorIdentity :: [Int] -> Bool)
it "should obey functor composition" $ do
property
(functorComposition :: (Fun Int String) -> (Fun String Int) -> [Int] -> Bool)
问题是,为了测试不同Functor实例的相同属性,我需要复制除[Int]
之外的所有内容:
testMaybeFunctorness :: IO ()
testMaybeFunctorness =
hspec $ do
describe "maybe" $ do
it "should obey functor identity" $ do
property (functorIdentity :: Maybe Int -> Bool)
it "should obey functor composition" $ do
property
(functorComposition :: (Fun Int String) -> (Fun String Int) -> Maybe Int -> Bool)
感觉我应该能够编写一个在不同Functor
个实例中具有某种多态性的表达式,但我甚至无法想到如何开始这样做。
如何方便地将该测试逻辑块重用于多个不同的Functor
?
答案 0 :(得分:3)
您可以做的是明确将所需类型传递给testFunctorness
:
import Data.Proxy
testFunctorness :: forall a. Functor a => Proxy a -> IO ()
testFunctorness _ =
hspec $ do
describe "the type" $ do
it "should obey functor identity" $ do
property (functorIdentity :: a Int -> Bool)
it "should obey functor composition" $ do
property
(functorComposition :: (Fun Int String) -> (Fun String Int) -> a Int -> Bool)
通话看起来像testFunctorness (Proxy :: Proxy [])
。
您需要启用{-# LANGUAGE ScopedTypeVariables #-}
,以便函数内的a
引用类型签名中的a
。然后需要forall
让typechecker知道这个a
应该是词法范围的。