如何使我的类型成为 Arbitrary 的实例?
How can I make my type an instance of Arbitrary?
我有以下数据和函数
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。当然这应该可以测试?
有两种合理的方法:
重用现有实例
如果有另一个看起来相似的实例,您可以使用它。 Gen
类型是 Functor
、Applicative
甚至 Monad
的实例,因此您可以轻松地从其他生成器构建生成器。这可能是编写 Arbitrary
个实例最重要的通用技术。大多数复杂实例将由一个或多个更简单的实例构建。
boolToFoo :: Bool -> Foo
boolToFoo False = A
boolToFoo True = B
instance Arbitrary Foo where
arbitrary = boolToFoo <$> arbitrary
在这种情况下,Foo
不能以任何有意义的方式 "shrunk" 到子部分,因此 shrink
的默认简单实现可以正常工作。如果它是一种更有趣的类型,您可以使用
的一些类似物
shrink = map boolToFoo . shrink . fooToBool
使用 Test.QuickCheck.Arbitrary
and/or 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 #-}
到文件的顶部。但是你不想要那个。无论如何,你肯定不想在这里。它仅适用于 class 具有基于泛型的完整默认值的 es,通常使用 DefaultSignatures
扩展。在这种情况下,Arbitrary
class 定义中没有 default arbitrary :: Generic a => Gen a
行,并且 arbitrary
是强制性的。因此,一旦 QuickCheck 尝试调用其 arbitrary
方法,DeriveAnyClass
生成的 Arbitrary
实例将产生 运行时错误。
我有以下数据和函数
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。当然这应该可以测试?
有两种合理的方法:
重用现有实例
如果有另一个看起来相似的实例,您可以使用它。 Gen
类型是 Functor
、Applicative
甚至 Monad
的实例,因此您可以轻松地从其他生成器构建生成器。这可能是编写 Arbitrary
个实例最重要的通用技术。大多数复杂实例将由一个或多个更简单的实例构建。
boolToFoo :: Bool -> Foo
boolToFoo False = A
boolToFoo True = B
instance Arbitrary Foo where
arbitrary = boolToFoo <$> arbitrary
在这种情况下,Foo
不能以任何有意义的方式 "shrunk" 到子部分,因此 shrink
的默认简单实现可以正常工作。如果它是一种更有趣的类型,您可以使用
shrink = map boolToFoo . shrink . fooToBool
使用 Test.QuickCheck.Arbitrary
and/or 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 #-}
到文件的顶部。但是你不想要那个。无论如何,你肯定不想在这里。它仅适用于 class 具有基于泛型的完整默认值的 es,通常使用 DefaultSignatures
扩展。在这种情况下,Arbitrary
class 定义中没有 default arbitrary :: Generic a => Gen a
行,并且 arbitrary
是强制性的。因此,一旦 QuickCheck 尝试调用其 arbitrary
方法,DeriveAnyClass
生成的 Arbitrary
实例将产生 运行时错误。