键入构造函数作为表达式的参数

时间:2017-12-15 11:22:14

标签: haskell

我使用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

1 个答案:

答案 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应该是词法范围的。