首页 > 解决方案 > 是否可以在 QuickCheck 中生成任意函数

问题描述

我试图为身份编写一个 QuickCheck 测试

f $ y = f y

我最初的计划是编写一个返回函数和整数的任意生成器,具有签名Gen (Int -> Int, Int)

并且在prop_DollerDoesNothing测试中,带有/不带有 的功能应用程序$给出了相同的结果。

这是我的代码:

  prop_DollarDoesNothing :: Property
  prop_DollarDoesNothing =
    forAll arbitraryFuncInt (\(f, y) -> (f $ y) == (f y))

  arbitraryFuncInt :: Gen (Int -> Int, Int)
  arbitraryFuncInt = do
    f <- elements [(\x -> x*2), (\x -> x+3), (\x -> x-2)]
    y <- arbitrary :: Gen Int
    return (f, y)

它生成了以下有用的错误消息:

    * No instance for (Show (Int -> Int))
        arising from a use of `forAll'
        (maybe you haven't applied a function to enough arguments?)
    * In the expression:
        forAll arbitraryFuncInt (\ (f, y) -> (f $ y) == (f y))
      In an equation for `prop_DollarDoesNothing':
          prop_DollarDoesNothing
            = forAll arbitraryFuncInt (\ (f, y) -> (f $ y) == (f y))

因此,我修复了错误并通过应用任意函数并从arbitraryFuncInt

  prop_DollarDoesNothing :: Property
  prop_DollarDoesNothing =
    forAll arbitraryFuncInt (\(x, y) -> x == y)

  arbitraryFuncInt :: Gen (Int, Int)
  arbitraryFuncInt = do
    f <- elements [(\x -> x*2), (\x -> x+3), (\x -> x-2)]
    y <- arbitrary :: Gen Int
    return (f $ y, f y)

我的问题是:

  1. 是否根本不可能返回由于没有实例而未完全应用的任意函数Show
  2. 我可以写一个实例Show (Int -> Int)# 1实现吗?
  3. QuickCheck 能否在给定类型签名的情况下生成任意函数,对于我正在测试所有函数(给定类型)的真实身份的情况。上面,我手动指定了 3 个测试功能,我想以某种方式自动化,理想情况下是这样的f <- arbitrary :: Gen (Int -> Int)

标签: haskellquickcheck

解决方案


QuickCheck 支持使用类型生成、缩小和显示函数FunCoArbitrary可以生成函数。然后将其转换为(可能是无限的)类似 trie 的结构,可以对其进行检查并缩小为有限值(因为测试失败仅取决于有限多个输入),然后可以将其显示为反例。

具体来说,您可以将属性编写为带有参数的函数,这是使用我描述的机制Fun的包装器。(->)用模式解构它Fn以获得一个函数。

prop_dollarDoesNothing :: Property
prop_dollarDoesNothing = property $ \(Fn (f :: Int -> Int)) x ->
  (f $ x) === f x

了解更多信息


推荐阅读