Scalacheck 尝试 : Monadic Associativity law passes with generated functions

标签 scala scalacheck

当我运行以下属性时,它通过了:

import org.scalacheck.Prop.forAll

import scala.util.Try

forAll { (m: Try[String], f: String => Try[Int], g: Int => Try[Double]) =>
    m.flatMap(f).flatMap(g) == m.flatMap(x => f(x).flatMap(g))

}.check // + OK, passed 100 tests.

但是它应该会失败,因为 Try 不是单子(monad)的(同样适用于“左身份”)

当未生成函数时,它按预期工作并且属性失败:

val f2 = (s: String) => Try { s.toInt }
val g2 = (i: Int) => Try { i / 2d }

forAll { (m: Try[String]) =>
    m.flatMap(f2).flatMap(g2) == m.flatMap(x => f2(x).flatMap(g2))
}.check  // ! Falsified after 0 passed tests.

为什么它不适用于生成的函数?

最佳答案

首先,单子(monad)结合律应该在这里通过。 Try 只违反了左恒等式:

unit(x).flatMap(f) == f(x)

因为 Try 永远不会抛出发生在它内部的异常(这是设计使然;为了更安全而愿意交换身份)。因此,如果 f 抛出异常,左侧将是一个失败的 Try,而右侧将简单地爆炸。

但是结合律:

m.flatMap(f).flatMap(g) == m.flatMap(x ⇒ f(x).flatMap(g))

应该持有。双方每次都应该成功或失败,但不可能炸毁一侧而不炸毁另一侧,因为 fg 都在两侧的 flatMap 中.

这里发生的是您的第二个代码段,即您自己定义函数的代码段,抛出了不同类型的异常。如果你只是打印出发生了什么:

...
  {
    val fst = m.flatMap(f).flatMap(g)
    val snd = m.flatMap(x => f(x).flatMap(g))
    println(fst)
    println(snd)
    fst == snd
  }
}.check
...

你自己看吧。这是第一种情况,具有未定义的函数:

// ...
// Failure(java.lang.Error)
// Failure(java.lang.Error)
// Success(5.16373771232299E267)
// Success(5.16373771232299E267)
// Failure(java.lang.Exception)
// Failure(java.lang.Exception)
// Failure(java.lang.Error)
// Failure(java.lang.Error)
// ...etc...

现在是第二种情况,具有定义的功能:

// ...
// Failure(java.lang.Error)
// Failure(java.lang.Error)
// Failure(java.lang.Exception)
// Failure(java.lang.Exception)
// Failure(java.lang.NumberFormatException: For input string: "걡圤")
// Failure(java.lang.NumberFormatException: For input string: "걡圤")

事情就是这样。第二个在某个时候为您的 f2 提供了一个奇怪的、中文字符填充的字符串;这种情况在第一种情况下永远不会发生。这与 Scalacheck 生成测试用例的方式有关。给定一个函数 String => Try[Int],它要么给出一个有效的整数,要么构成一些通用异常以创建一个失败案例。它不会使用在 String 上定义的具体函数(例如您使用的 toInt)。

因此,第二种情况会导致数字格式异常。为什么第一个场景中的异常在每次比较时都会产生 true,而第二个场景中的数字格式异常在比较时会产生 false?我将把这个留给 Java 专家。我认为这与 == 依赖于 Java 的 equals 这一事实有关,它比较引用,但 null == null 产生 true,所以我想在某些时候比较内部异常字段,第一个场景到处都是空值(记住,这些异常是一般的,因为它们是由 Scalacheck 组成的),第二个场景有实际的异常(更具体地说, java.lang.NumberFormatException) 中包含实际对象,这会导致看似相同的异常在相等比较时产生 false。尝试将测试条件从 fst == snd 更改为 fst.toString == snd.toString,您会发现这两种情况都会通过。

很抱歉没有提供 100% 完整的答案,但我需要投入大量时间来调试愚蠢的 Java 对象和引用处理以及如何在各种级别和异常类上实现相等性,更不用说整个“null == null is true”哲学困境。如果您(像我一样)对 Java 怪癖不那么感兴趣,那么我想这可以回答您的问题。

关于Scalacheck 尝试 : Monadic Associativity law passes with generated functions,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49233424/

相关文章:

Scala Play框架2.1派生类

scala - 如何传递带有隐式参数的函数

java - 如何将 s3/Hdfs 中的文件附加到 Spark-Scala 中的电子邮件?

scalacheck - 如何使用 scalacheck 生成器生成时间?

scala - 在 ScalaCheck 中创建了 unicode 和没有空格生成器的 unicode

scala - 使用andThe减少不同类型的函数

scala - Manifest[T].erasure 在 2.10 中已弃用,我现在应该使用什么?

scalacheck:生成一个非空字符串

java - 在 Java 中随机生成*有趣的*字符串

scala - 正数的性质不应缩减为负数