【问题标题】:How can I make my type an instance of Arbitrary?如何使我的类型成为任意的实例?
【发布时间】:2016-02-21 22:06:01
【问题描述】:

我有以下数据和函数

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 deriving (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。这应该可以测试吗?

【问题讨论】:

    标签: haskell generics testing quickcheck


    【解决方案1】:

    有两种合理的做法:

    重用现有实例

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

    boolToFoo :: Bool -> Foo
    boolToFoo False = A
    boolToFoo True = B
    
    instance Arbitrary Foo where
      arbitrary = boolToFoo <$> arbitrary
    

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

      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
    

    如前所述,在这种情况下,默认的 shrink 实现就可以了。对于递归类型,您可能需要添加

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

    然后为您的类型派生Generic 并使用

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

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


    您询问了DeriveAnyClass。如果你想要,你会添加

    {-# LANGUAGE DeriveAnyClass #-}
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-04-10
      • 1970-01-01
      • 2014-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多