Haskell QuickCheck缺少Natural和Dyadic的Arbitrary实例如何解决?
问题原因和最优解决方案
- QuickCheck核心包不内置
Natural的Arbitrary实例,这类非Prelude标准类型的实例都拆分在quickcheck-instances扩展包中,需要额外引入依赖并导入对应实例模块。 - 最符合Haskell开发习惯的方案是直接为自定义的
Dyadic类型实现Arbitrary实例,一次性解决两类报错,后续写其他属性测试也更简洁。
操作步骤
步骤1:添加项目依赖
在你的项目cabal文件的build-depends字段中加入两个依赖:
build-depends: base >=4.14 && <5, QuickCheck >=2.14, quickcheck-instances >=0.3
如果用Stack管理项目,对应在stack.yaml的extra-deps中确认存在这两个包即可。
步骤2:导入必要模块
在测试文件头部补充导入两个模块:
-- 导入Arbitrary类型类 import Test.QuickCheck.Arbitrary -- 导入Natural的Arbitrary实例,不需要导入具体函数所以写空导入 import Test.QuickCheck.Instances.Natural ()
步骤3:为Dyadic实现Arbitrary实例
可以直接在测试文件中写实例(不需要改Dyadic类型的定义模块,属于孤儿实例,测试场景下是完全可接受的):
instance Arbitrary Dyadic where -- 直接复用Integer和Natural的任意值生成逻辑构造Dyadic arbitrary = Dyadic <$> arbitrary <*> arbitrary -- 可选实现shrink方法:测试失败时自动收缩到最小复现用例,大幅提升调试效率 shrink (Dyadic n p) = uncurry Dyadic <$> shrink (n, p)
步骤4:改回简洁的测试函数签名
你最开始写的Dyadic -> Dyadic -> Bool的签名是完全可用的,不需要拆成四个参数:
dyadic_add :: Dyadic -> Dyadic -> Bool dyadic_add d1 d2 = dyadic_to_rational (d1 `plus` d2) == dyadic_to_rational d1 + dyadic_to_rational d2
另外注意你的Dyadic类型定义需要派生Eq和Show,否则hspec无法判断测试结果、也无法打印失败的用例:
data Dyadic = Dyadic Integer Natural deriving (Eq, Show)
如果不想给Dyadic写Arbitrary实例,只想临时解决Natural的报错,只要完成步骤1和导入Test.QuickCheck.Instances.Natural ()即可,无需写自定义实例,但显然给Dyadic加实例的方案复用性更高、代码更简洁。
内容的提问来源于stack exchange,提问作者Alex Altair
相关产品推荐
相关产品推荐

