【问题标题】:How does QuickCheck detect datatypes?QuickCheck 如何检测数据类型?
【发布时间】:2019-11-07 12:25:52
【问题描述】:

如果我们定义这样的函数

fun :: Int -> Property

然后运行

quickCheck fun

quickCheck 开始生成 Int 类型的随机数据。问题是 quickCheck 如何检测 fun 的参数数据类型是 Int 而不是任何其他数据类型?如果我把问题说得更笼统,我应该问一下,如果我们有这样一个名为 fun 的函数

fun :: datatype_1 -> datatype_2 -> ... -> datatype_n -> Property

quickCheck 如何检测每个单独的 datatype_1、datatype_2、... 和 datatype_n 的类型?以及它如何检测函数 fun 需要多少个参数?

【问题讨论】:

  • 它不必检测它,它只是将其限制为Arbitrary 的实例。
  • 至于数据类型的数量。在 Haskell 中,每个函数都恰好接受一个参数。因此,它使用某种递归来递归从该函数返回的函数,等等。

标签: haskell quickcheck


【解决方案1】:

大致来说,这就是类型类的工作方式。可以声明

class C a where
   foo :: a -> Bool

然后

instance C (Int -> Bool) where
   foo f = f 42
instance C (String -> Bool) where
   foo f = f "hello"
instance C (String -> [Int]) where
   foo f = sum (f "hello") > 42

等等。

这具有使foo“检测”其参数f的类型并采取相应措施的明显效果。实际上,发生的事情是 Haskell 执行类型推断,在此期间选择适当的实例——在编译时。在运行时,不会发生“类型检测”;事实上,类型在编译后会被擦除,并且在运行时没有可用的类型信息,因此无法检测到f 属于哪个类型。

当然,实际的 QuickCheck 机制要复杂得多。为了处理具有任意数量参数的函数,可以使用一组“递归”instances,可以说,每次“递归调用”处理每个参数。这是一种相当棘手的技术,也用于printf 和其他“可变参数”函数。如果您对类型类不熟悉,我不建议您从如此复杂的技巧开始学习它们。

【讨论】:

    【解决方案2】:

    聚会有点晚了,但this is the instance 您正在当前的实施中寻找。

    instance (Arbitrary a, Show a, Testable prop) => Testable (a -> prop) where
      property f =
        propertyForAllShrinkShow arbitrary shrink (return . show) f
      propertyForAllShrinkShow gen shr shw f =
        -- gen :: Gen b, shr :: b -> [b], f :: b -> a -> prop
        -- Idea: Generate and shrink (b, a) as a pair
        propertyForAllShrinkShow
          (liftM2 (,) gen arbitrary)
          (liftShrink2 shr shrink)
          (\(x, y) -> shw x ++ [show y])
          (uncurry f)
    

    正如@chi 正确指出的那样,这里发生了递归。递归调用是propertyForAllShrinkShow 调用propertyForAllShrinkShow,通过调用uncurrya -> b -> c -> Bool 形式的属性变成(a, b) -> c -> Bool。由于(a, b) 是一个有效的任意值,因为存在Arbitrary (a, b)instance,所以Testable 的相同实例将再次运行,其中propc -> Bool。然后,同样会以((a, b), c 再次运行,而prop 将只是Bool。此时,Bool instance of Testable 启动,它使用默认的propertyForAllShrinkShow,它创建了f x 的实际应用程序。所以另一种说法是,quickcheck 同时生成所有值作为任意元组,并在Testableinstance 中使用递归来构造元组。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多