You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QuickCheck测试函数类型Combine的Semigroup结合律及相关疑问

问题解答

1. Combine的Arbitrary实例与CoArbitrary作用原理

你写的Arbitrary (Combine a b)实例本质是复用了QuickCheck内置的函数类型a -> b的Arbitrary实例,只是在外层套了Combine的newtype包装器,Combine <$> arbitrary等价于把随机生成的a -> b函数封装成Combine类型的值,所以整个实例非常简洁。

CoArbitrary的作用是支撑随机函数的生成:
QuickCheck要生成符合逻辑的随机函数a -> b,核心要满足「不同的输入a可以得到不同的输出b」,这就需要一种能力:把a类型的值作为扰动因子,修改随机生成器的种子,从而得到不同的b类型随机值。CoArbitrary类型类就是定义这种能力的,它的核心方法是:

coarbitrary :: CoArbitrary a => a -> Gen r -> Gen r

这个方法接收一个a类型的值,把传入的生成器做一次种子扰动后返回。只要a实现了CoArbitrary,b实现了Arbitrary,QuickCheck就可以自动生成随机的a -> b函数:对于任意输入的a,先用coarbitrary a扰动生成器,再用扰动后的生成器生成b类型的输出,就得到了一个随机函数的行为。

你用到的Combine String String场景里,String已经内置了CoArbitrary实例,String也有Arbitrary实例,完全满足实例约束,所以可以正常生成随机的Combine值。

2. Combine类型的Show实例优化方案

Haskell的函数是运行时的闭包结构,默认不携带源代码、逻辑描述等元信息,所以不可能直接打印出函数的内部实现逻辑,但有几种实用的替代方案可以满足调试、测试需求:

  • 方案1:利用QuickCheck的错误打印能力,不需要自定义Show实例
    测试失败时你需要的其实是「调用参数、两边计算结果」这类调试信息,不需要打印Combine本身,你可以修改测试属性,用counterexample打印需要的信息:
    import Test.QuickCheck (counterexample, Property, forAll, arbitrary)
    
    combineSemigroupAssoc :: (Eq b, Semigroup b, Show a, Show b) => a -> Combine a b -> Combine a b -> Combine a b -> Property
    combineSemigroupAssoc x a b c = 
      let left = unCombine (a <> (b <> c)) x
          right = unCombine ((a <> b) <> c) x
      in counterexample ("输入参数x: " ++ show x) $
         counterexample ("左边计算结果: " ++ show left) $
         counterexample ("右边计算结果: " ++ show right) $
         left == right
    
    测试失败时会自动打印出上面的所有信息,完全可以定位问题。
  • 方案2:侵入式带标签的Combine定义(仅用于测试场景)
    如果你确实需要给Combine加可识别的Show信息,可以修改Combine的定义,新增一个标签字段存函数描述:
    newtype Combine a b = Combine { unCombine :: a -> b, desc :: String }
    
    instance Show (Combine a b) where
      show = desc
    
    生成Arbitrary实例的时候可以自动生成随机的描述字符串,不过这种方式会修改原类型定义,只适合测试用,不建议生产环境用。
  • 方案3:导入Text.Show.Functions模块
    这个模块内置了函数类型的Show实例,会打印<function>,比你现在的实现更通用,不需要自己写Combine的Show实例,但也不会提供更多信息。

至于你说的查看实际调用参数,用方案1的counterexample就可以把所有用到的参数、计算结果都打印出来,不需要依赖Combine的Show实例。

内容的提问来源于stack exchange,提问作者Calin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 13:45:03