首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用shapeless scalacheck派生任意函数实例

使用shapeless scalacheck派生任意函数实例
EN

Stack Overflow用户
提问于 2016-11-03 06:25:02
回答 2查看 770关注 0票数 4

在升级到scalacheck 1.13.3之后,我遇到了一个奇怪的问题,在那里派生A => B Or C的实例几乎总是会失败,而Or本质上是一个轻量级的Either

这是我能写的重现这个问题的最简单的代码:

代码语言:javascript
复制
import org.scalatest.FunSuite
import org.scalatest.prop.GeneratorDrivenPropertyChecks
import org.scalacheck.Shapeless._

class Testor extends FunSuite with GeneratorDrivenPropertyChecks {
  sealed trait Or[+A, +B] extends Product with Serializable
  case class Left[A](a: A) extends Or[A, Nothing]
  case class Right[B](b: B) extends Or[Nothing, B]

  test("reproduce") {
    forAll { (i: Int, f: Int ⇒ Float Or Boolean) ⇒
      f(i)
    }
  }
}

此操作失败,出现以下错误:

代码语言:javascript
复制
RetrievalError was thrown during property evaluation.
  Message: couldn't generate value
  Occurred when passed generated values (
    arg0 = 0, // 30 shrinks
    arg1 = <function1>
    )

请注意,提供显式的Arbitrary[Float Or Boolean]解决了这个问题,因此很明显问题出在泛型派生中。

我不相信问题出在shapeless-scalacheck上--我试着写我自己的泛型派生,看看它是否有帮助,但以完全相同的方式失败了。

有些奇怪的事情,可能是由于任意函数的工作方式,函数实际上是生成的,但在计算时失败了。

感谢大家对此的帮助/建议,因为我有点被卡住了。

EN

回答 2

Stack Overflow用户

发布于 2016-11-04 07:16:22

这里的问题似乎是两面性的:

  • 默认测试参数在scalacheck (org.scalacheck.Test.Parameters.default)和scalatest中,minSize0。在生成任意余积时,Prop.check尝试在start up.
  • MkCoproductArbitrary.ccons中生成具有此大小的值--在生成任意余积时,将大小解释为零作为终止条件,因此无法生成。

作为临时解决方法,在Travis's response中的示例中,可以通过以下方式检查属性:

代码语言:javascript
复制
prop.check(Test.Parameters.default.withMinSize(1))

使用scalatest (在问题中使用),最小大小参数也可以更改,可能是这样

代码语言:javascript
复制
implicit val config = PropertyCheckConfiguration(minSize = PosZInt(1))

在测试用例之前(警告:我没有尝试/检查最具伸缩性的解决方案,不像之前的纯scalacheck解决方案)。

票数 3
EN

Stack Overflow用户

发布于 2016-11-04 04:15:00

这是一个巧妙的问题,我没有真正的解决方案,但它类似于我现在遇到的一些其他问题,因为我们在ScalaCheck 1.13中为函数提供了很好的Arbitrary实例。

首先,这里有一个不依赖于ScalaTest的最小化:

代码语言:javascript
复制
import org.scalacheck._, Shapeless._

sealed trait Foo; case object Bar extends Foo

val prop = Prop.forAll { (f: Int => Foo) => f(0); true }

然后:

代码语言:javascript
复制
scala> prop.check
! Exception raised on property evaluation.
> ARG_0: <function1>
> Exception: org.scalacheck.Gen$RetrievalError: couldn't generate value
org.scalacheck.Gen.loop$1(Gen.scala:57)
org.scalacheck.Gen.doPureApply(Gen.scala:58)
...

当我以前在1.13中遇到过这样的东西时,核心问题总是生成器在给定的大小为零时失败,而事实上,罪魁祸首似乎是this line中的标量形的case 0 => Gen.fail

我们需要询问Alexandre来确认,但这似乎是一次事后的尝试,以避免递归ADT上的堆栈溢出。上下文是这样的:

代码语言:javascript
复制
Gen.sized {
  case 0 => Gen.fail
  case size =>
    val sig = math.signum(size)

    Gen.frequency(
      1   -> Gen.resize(size - sig, Gen.lzy(headArbitrary.value.arbitrary)).map(Inl(_)),
      n() -> Gen.resize(size - sig, Gen.lzy(tailArbitrary.arbitrary.arbitrary)).map(Inr(_))
    )
}

在当前的实现中,sig将始终为1,所以我猜最初case 0并不存在,其意图是做一些类似math.min(0, size - 1)的事情(对于非负整数,它将始终等于size - math.signum(size) )。

如果删除case 0 => Gen.fail行(或者将最小大小设置为大于零的值),代码就可以正常工作。问题是,如果没有case 0行,这样的ADT的派生实例可能会溢出堆栈:

代码语言:javascript
复制
sealed abstract class Tree
final case class Node(left: Tree, right: Tree, v: Int) extends Tree
case object Leaf extends Tree

…因为即使size0,您也可以继续选择递归Node分支。

您想要的是能够在size为零时说出类似“给我一个值,但不是来自递归构造函数”之类的语句,而乍一看,我看不出有什么方法可以实现这一点。

显然,在我们新的Cogen-ful世界里,case 0 => Gen.fail是不好的。如果我的库是scalacheck-shapeless,我可能会做一些可怕的事情,比如删除该行,然后捕获递归ADT的StackOverflowErrorGen.fail。这将是一个黑客,但它仍然比目前的情况更好。

我们可能应该在一个无标量的问题上讨论这个问题。同时,我只需要手动编写OrArbitrary (您应该能够派生出RightLeft,所以这并不是太糟糕)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40390846

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档