【问题标题】:Type constructor as argument to expression类型构造函数作为表达式的参数
【发布时间】:2017-12-19 01:16:01
【问题描述】:

我正在使用 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]s 之外的所有内容:

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 实例上以某种方式具有多态性的表达式,但我什至想不出如何开始。

如何方便地为多个不同的Functors 重用该测试逻辑块?

【问题讨论】:

  • 我 99% 确定我在这里遇到了 AB 问题,但我看不到 B 是什么 :)

标签: haskell


【解决方案1】:

您可以做的是将所需类型显式传递给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 让类型检查器知道这个 a 应该是词法范围的。

【讨论】:

  • 啊,Data.Proxy 很棒。我最终在我的约束中使用了Arbitrary (a Int), Show (a Int), Eq (a Int) 来启动 QuickCheck,但除此之外它非常完美。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多