haskell - 如何使我的类型成为 Arbitrary 的实例?

标签 haskell generics testing quickcheck

我有以下数据和函数

data Foo = A | B deriving (Show)

foolist :: Maybe Foo -> [Foo]
foolist Nothing  = [A]
foolist (Just x) = [x]

prop_foolist x = (length (foolist x)) == 1

当运行 quickCheck prop_foolist 时,ghc 告诉我 Foo 需要是 Arbitrary 的一个实例。

No instance for (Arbitrary Foo) arising from a use of  ‘quickCheck’
In the expression: quickCheck prop_foolist
In an equation for ‘it’: it = quickCheck prop_foolist

我试过 data Foo = A | B 派生 (Show, Arbitrary),但这导致

Can't make a derived instance of ‘Arbitrary Foo’:
  ‘Arbitrary’ is not a derivable class
  Try enabling DeriveAnyClass
In the data declaration for ‘Foo’

但是,我不知道如何启用 DeriveAnyClass。我只是想通过我的简单功能使用快速检查! x 的可能值是 Nothing、Just A 和 Just B。当然这应该可以测试?

最佳答案

有两种合理的方法:

重用现有实例

如果有另一个看起来相似的实例,您可以使用它Gen 类型是 FunctorApplicative 甚至 Monad 的实例,因此您可以轻松地从其他的。这可能是编写 Arbitrary 实例最重要的通用技术。大多数复杂实例将由一个或多个更简单的实例构建。

boolToFoo :: Bool -> Foo
boolToFoo False = A
boolToFoo True = B

instance Arbitrary Foo where
  arbitrary = boolToFoo <$> arbitrary

在这种情况下,Foo 不能以任何有意义的方式“收缩”为子部分,因此 shr​​ink 的默认简单实现可以正常工作。如果它是一个更有趣的类型,你可以使用一些类似的

  shrink = map boolToFoo . shrink . fooToBool

使用 Test.QuickCheck.Arbitrary 和/或 Test.QuickCheck.Gen

中可用的部分

在这种情况下,将各个部分放在一起非常容易:

import Test.QuickCheck.Arbitrary

data Foo = A | B
  deriving (Show,Enum,Bounded)
instance Arbitrary Foo where
  arbitrary = arbitraryBoundedEnum

如前所述,默认的 shr​​ink 实现在这种情况下就可以了。在递归类型的情况下,您可能想要添加

{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics (Generic)

然后根据您的类型和用途派生Generic

instance Arbitrary ... where
  ...
  shrink = genericShrink

正如文档警告的那样,genericShrink 不遵守您可能希望强加的任何内部有效性条件,因此在某些情况下可能需要小心。


您询问了关于 DeriveAnyClass 的问题。如果你想要那个,你会添加

{-# LANGUAGE DeriveAnyClass #-}

到文件的顶部。但是你不想要那个。无论如何,你肯定不想在这里。它仅适用于具有基于泛型的完整默认值补充的类,通常使用 DefaultSignatures 扩展。在这种情况下,Arbitrary 类定义中没有 default arbitrary::Generic a => Gen a 行,arbitrary 是必需的。因此,一旦 QuickCheck 尝试调用其 arbitraryDeriveAnyClass 生成的 Arbitrary 实例将产生一个运行时错误方法。

关于haskell - 如何使我的类型成为 Arbitrary 的实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35542734/

相关文章:

Haskell:尝试使用两个参数定义函数时出错

scala - Scala 中的方法泛化

java - 对集合进行单元测试的最佳方法?

haskell - 在 HXT 中使用列表

haskell - 循环一个值 (Enum a, Bounded a) => a

具有复合/多参数类型构造函数的 Haskell 类型签名

.net - 访问泛型类的静态属性?

Java 泛型 - 使用具有相同类名的类型实现具有两个泛型类型的方法

testing - 在 Jenkins 中显示单个测试结果的历史记录 - 附加插件或配置问题?

perl- Selenium : like() or $sel->like()?