你为什么需要在scalacheck中使用Arbitraries?

时间:2015-06-29 08:32:03

标签: scala random scalacheck

我想知道为什么需要Arbitrary,因为自动属性测试需要属性定义,比如

val prop = forAll(v: T => check that property holds for v)

和值v生成器。用户指南说您可以为自定义类型创建自定义生成器(例如,树的生成器)。然而,它并没有解释为什么你需要最重要的仲裁。

这是一本手册

implicit lazy val arbBool: Arbitrary[Boolean] = Arbitrary(oneOf(true, false))
  

要获得对您自己的类型T的支持,您需要定义隐式def   或任意类型的T [T]。使用工厂方法任意(...)来   创建任意实例。此方法采用一个类型的参数   Gen [T]并返回一个任意[T]的实例。

它清楚地表明我们需要在Gen之上任意。任意的理由不尽如人意,但

  

任意生成器是ScalaCheck使用的生成器   生成属性参数的值。

IMO,要使用生成器,您需要导入它们而不是将它们包装到仲裁中!否则,我们可以争辩说我们需要将仲裁包装到其他东西中以使它们可用(等等无限制地无限地包装包装)。

您还可以解释arbitrary[Int]如何将参数类型转换为生成器。这很奇怪,我觉得这些是相关的问题。

3 个答案:

答案 0 :(得分:20)

forAll { v: T => ... }是在Scala implicits 的帮助下实现的。这意味着类型T的生成器是隐式发现的,而不是由调用者显式指定。

Scala隐含很方便,但如果您不确定当前隐含值或转换的范围,它们也会很麻烦。通过使用特定类型(Arbitrary)进行隐式查找,ScalaCheck尝试约束使用implicits的负面影响(这种使用也使其类似于某些用户所熟悉的Haskell 类型类 )。

所以,你完全正确,Arbitrary并不是真的需要。通过隐式Gen[T]值可以实现相同的效果,可以说是更隐含的范围混淆。

作为最终用户,您应该将Arbitrary[T]视为类型T默认生成器。您可以(通过作用域)定义和使用多个Arbitrary[T]实例,但我不推荐它。相反,只需跳过Arbitrary并明确指定您的生成器:

val myGen1: Gen[T] = ...
val mygen2: Gen[T] = ...

val prop1 = forAll(myGen1) { t => ... }
val prop2 = forAll(myGen2) { t => ... }

arbitrary[Int]就像forAll { n: Int => ... }一样,只是查找隐式Arbitrary[Int]实例并使用其生成器。实施很简单:

def arbitrary[T](implicit a: Arbitrary[T]): Gen[T] = a.arbitrary

Arbitrary的实施在这里也可能有所帮助:

sealed abstract class Arbitrary[T] {
  val arbitrary: Gen[T]
}

答案 1 :(得分:8)

ScalaCheck已从Haskell QuickCheck library移植。在Haskell类型类中,只允许给定类型的一个实例,从而迫使您进入这种分离。 但是在Scala中,没有这样的约束,可以简化库。我的猜测是,ScalaCheck(最初编写为)为QuickCheck的1-1映射,使Haskellers更容易跳入Scala:)

以下是任意

的Haskell定义
var section = configuration.GetSection($"{APP_SETTINGS_SECTION}:{APP_SETTINGS_KEY}");
var folders = section.Get<string[]>();

和Gen

class Arbitrary a where
  -- | A generator for values of the given type.
  arbitrary :: Gen a

正如你所看到的,它们有一个非常不同的语义,Arbitrary是一个类型类,而Gen是一个带有一堆组合器的包装器来构建它们。

我同意“通过语义限制范围”的论点有点模糊,在组织代码时似乎没有被认真对待:任意类有时简单地委托给Gen实例,如

newtype Gen a

有时会定义自己的生成器

/** Arbirtrary instance of Calendar */
implicit lazy val arbCalendar: Arbitrary[java.util.Calendar] =
  Arbitrary(Gen.calendar)

所以实际上这会导致代码重复(每个默认的Gen都被任意一个镜像)和一些混淆(为什么/** Arbitrary BigInt */ implicit lazy val arbBigInt: Arbitrary[BigInt] = { val long: Gen[Long] = Gen.choose(Long.MinValue, Long.MaxValue).map(x => if (x == 0) 1L else x) val gen1: Gen[BigInt] = for { x <- long } yield BigInt(x) /* ... */ Arbitrary(frequency((5, gen0), (5, gen1), (4, gen2), (3, gen3), (2, gen4))) } 没有包装默认的Arbitrary[BigInt]?)。

答案 2 :(得分:0)

我对此的解读是,您可能需要拥有Gen的多个实例,因此Arbitrary用于&#34;标记&#34;你希望ScalaCheck使用的那个?