Haskell 中的任意值

CoArbitrary in Haskell

我正在阅读 Haskell 这本书,并且已经到了为 newType Comp 编写任意实例的地步。代码如下

instance Show (Comp a) where
    show f = "Unicorns!!"

newtype Comp a =
    Comp { unComp :: (a -> a) }

instance (Semigroup a) => S.Semigroup (Comp a) where
    (Comp fx) <> (Comp fy) = Comp (fx . fy)

instance (CoArbitrary a, Arbitrary a) => Arbitrary (Comp a) where
    arbitrary = do
        f <- Test.QuickCheck.arbitrary
        return (Comp f)

type CompAssoc = String -> Comp String -> Comp String -> Comp String -> Bool

compAssoc :: (S.Semigroup a, Eq a) => a -> Comp a -> Comp a -> Comp a -> Bool
compAssoc v a b c = (unComp (a <> (b <> c)) $ v) == (unComp ((a <> b) <> c) $ v)

并用

测试
main :: IO ()
main = do
    quickCheck (compAssoc :: CompAssoc)

我的问题围绕着 Arbitrary 实例。它正在生成要传递给 return (Comp f) 的函数。我明白(虽然不完全是为什么)这必须限定在 CoArbitrary 范围内。但是传给return (Comp f)的那个东西如果是CoArbitrary,怎么可能也是Arbitrary呢?我猜这些约束似乎都指的是函数的 pass/return 类型 和函数本身 。我有点困惑。

这是初学者的高级,很可能底层细节不正确对[=10=之间关系的解释] 和 CoArbitrary

文章 https://begriffs.com/posts/2017-01-14-design-use-quickcheck.html 给了你更好的解释,但它使用了 Applicative 和 Functor,我还没有到达那里,因为我还在 "The Haskell Book" 的 SemiGroup 章节。

如果要从 a -> b 生成函数,b 必须具有 Arbitrary 的实例。无论如何我们都会生成随机的东西,所以我们像往常一样需要 Gen b。但是我们需要 vary 这个 b 就像普通函数一样,也就是说,如果我将不同的值传递给像 double 这样的函数,我希望得到不同的回应。但是我们如何改变一个基于随机a的生成器呢?好吧,如果我们首先考虑我们生成随机 b 的方式,它与 numbers 有关。 IE。如果我想要一个随机的 b(例如 Int),我只生成一个随机数。十分简单。随机字符串?从随机数开始,将它们转换为 ASCII 字符。与您想要的任何其他数据类型一样,quickcheck 提供了很多 Arbitrary 实例来做到这一点。

好的,所以最基本的是数字。 我们可以通过获取其他数字来更改这些数字,例如 multiplying/subtracting/adding/dividing。或任何其他数学运算。但是我们如何得到另一个数字呢?这就是 CoArbitrary 的用武之地!如果我将类型标记为需要 CoArbitrary 的实例,实际上我是在说“我需要能够将您的类型减少到一个数字,以便我可以 变化 (add/mult/etc) 类型 b 的生成器。因此,如果类型 a 是一个字符串,例如,将该字符串简化为一个数字,将其传递给 [=27] 的生成器=] 为了让类型 b 的 Gen 对 number(s) 执行 mult/add/etc 操作,它将用来生成 b .