【问题标题】:DRY type annotation for QuickCheck propertiesQuickCheck 属性的 DRY 类型注释
【发布时间】:2018-10-03 12:57:03
【问题描述】:

使用 QuickCheck,可以编写参数化多态属性,如下所示:

associativityLaw :: (Eq a, Show a, Semigroup a) => a -> a -> a -> Property
associativityLaw x y z = (x <> y) <> z === x <> (y <> z)

这只是一个例子,因为我的实际属性更复杂,但它很好地说明了问题。此属性验证对于 a 类型,&lt;&gt; 运算符是关联的。

想象一下,我想为不止一种类型使用此属性。我可以这样定义我的测试列表:

tests =
  [
    testGroup "Monoid laws" [
      testProperty "Associativity law, [Int]" (associativityLaw :: [Int] -> [Int] -> [Int] -> Property),
      testProperty "Associativity law, Sum Int" (associativityLaw :: Sum Int -> Sum Int -> Sum Int -> Property)
    ]
  ]

这可行,但感觉不必要地冗长。我希望能够简单地说明对于给定的属性,a 应该是 [Int],或者 a 应该是 Sum Int

类似这样的假设语法:

testProperty "Associativity law, [Int]" (associativityLaw :: a = [Int]),
testProperty "Associativity law, Sum Int" (associativityLaw :: a = Sum Int)

有没有办法做到这一点,也许使用 GHC 语言扩展?

我的实际问题涉及更高种类的类型,我希望能够说明这一点,例如f a[Int],或者f aMaybe String

我知道this answer,但至少如其中所述,这两个选项(ProxyTagged)似乎都太尴尬而无法真正解决问题。

【问题讨论】:

  • 我想我宁愿明确指定生成器而不是默认任意类型。我用here 的例子做了一个要点。当然,它在 Hedgehog 中,但可以移植到 QC。 /cc @dcastro

标签: haskell-stack quickcheck


【解决方案1】:

您可以使用TypeApplications 来绑定类型变量,如下所示:

{-# LANGUAGE TypeApplications #-}

associativityLaw @[Int]

如果你提到你有更高种类的类型并且你想将f a绑定到[Int],你必须分别绑定类型变量fa

fmap @[] @Int

对于具有多个类型变量的函数,您可以按顺序应用 args:

f :: a -> b -> Int

-- bind both type vars    
f @Int @String

-- bind just the first type var, and let GHC infer the second one
f @Int

-- bind just the second type var, and let GHC infer the first one
f @_ @String

有时类型变量的“顺序”可能并不明显,但您可以使用:type +v 并向 GHCi 询问更多信息:

λ> :t +v traverse
traverse
  :: Traversable t =>
     forall (f :: * -> *) a b.
     Applicative f =>
     (a -> f b) -> t a -> f (t b)

在标准的 haskell 中,类型变量的“顺序”无关紧要,所以 GHC 只是为您补了一个。但是在TypeApplications 面前,顺序确实很重要:

map :: forall b a. (a -> b) -> ([a] -> [b])
-- is not the same as
map :: forall a b. (a -> b) -> ([a] -> [b])

因此,在使用高度参数化的代码时,或者您希望您的用户希望在您的函数上使用 TypeApplications,您可能希望显式设置类型变量的顺序,而不是让 GHC 定义一个为您订购,ExplicitForAll:

{-# LANGUAGE ExplicitForAll #-}

map :: forall a b. (a -> b) -> ([a] -> [b])

感觉很像java或c#中的&lt;T1, T2&gt;

【讨论】:

    猜你喜欢
    • 2019-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-13
    • 1970-01-01
    • 2011-05-21
    • 2016-04-24
    相关资源
    最近更新 更多