因为它已经存在,所以无法为`Char`定义自定义`Arbitrary`实例

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 aa).有一个明显的Monoid for Lists实例:

class Monoid [a] where
    mempty  = []
    mappend = (++)
Run Code Online (Sandbox Code Playgroud)

定义Ints 也很容易.实际上,具有加法运算的整数形成了正确的幺半群.

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包装器,即SumProduct.

更进一步,你的发言

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.因此,教程中的代码在当时完全有效.