Tur*_*ion 6 haskell quickcheck
我尝试按照Quickcheck简介进行操作,并想测试我的函数,该函数包含包含数字的字符串.为此,我定义了一个Arbitrary
实例Char
:
instance Arbitrary Char where
arbitrary = choose ('0', '9')
Run Code Online (Sandbox Code Playgroud)
但是ghc抱怨说:
A.hs:16:10:
Duplicate instance declarations:
instance Arbitrary Char -- Defined at A.hs:16:10
instance [overlap ok] [safe] Arbitrary Char
-- Defined in ‘Test.QuickCheck.Arbitrary’
Run Code Online (Sandbox Code Playgroud)
我怎么能告诉它忘记已定义的实例并使用我自己的实例?或者它根本不会那样工作(这会很奇怪,因为教程采用了这种方法)?
zud*_*dov 11
正如@ carsten-könig所建议的那样,解决方案是制作newtype
包装纸Char
.这不是一种解决方法,而是一种正确且非常好的方法来逃避与孤儿实例相关的整类问题(在另一个模块中定义的类型类的实例),在这里阅读更多有关此类问题的信息.
此外,当存在具有不同行为的若干可能实例时,这种方法被广泛使用.
例如,考虑在以下Monoid
定义的类型类Data.Monoid
:
class Monoid a where
mempty :: a -- ^ Identity of 'mappend'
mappend :: a -> a -> a -- ^ An associative operation
Run Code Online (Sandbox Code Playgroud)
您可能已经知道,Monoid是一种可以相互附加(使用mappend
)的值mempty
,并且存在满足规则的'identity'值mappend mempty a == a
(将值附加到value a
中a
).有一个明显的Monoid for Lists实例:
class Monoid [a] where
mempty = []
mappend = (++)
Run Code Online (Sandbox Code Playgroud)
定义Int
s 也很容易.实际上,具有加法运算的整数形成了正确的幺半群.
class Monoid Int where
mempty = 0
mappend = (+)
Run Code Online (Sandbox Code Playgroud)
但这是整数唯一可能的幺半群吗?当然不是,整数乘法会形成另一个合适的幺半群:
class Monoid Int where
mempty = 1
mappend = (*)
Run Code Online (Sandbox Code Playgroud)
这两个实例都是正确的,但现在我们遇到了一个问题:如果你想尝试评估1 `mappend` 2
,就无法确定必须使用哪个实例.
这就是为什么Data.Monoid
将数字的实例包装成newtype包装器,即Sum
和Product
.
更进一步,你的发言
instance Arbitrary Char where
arbitrary = choose ('0', '9')
Run Code Online (Sandbox Code Playgroud)
可能会很混乱.它说"我是一个任意角色",但只产生数字字符.在我看来,这会好得多:
newtype DigitChar = DigitChar Char deriving (Eq, Show)
instance Arbitrary DigitChar where
arbitrary = fmap DigitChar (choose ('0', '9'))
Run Code Online (Sandbox Code Playgroud)
一块蛋糕.您可以进一步隐藏DigitChar
构造函数,提供digitChar
"智能构造函数",这将不允许创建DigitChar
实际上不是数字的构造函数.
至于你的问题:"你知道为什么这不是教程采取的方法?",我认为原因很简单,本教程似乎在2006年写的,在那些日子里,快速检查根本没有定义Arbitrary
,例如Char
.因此,教程中的代码在当时完全有效.