Scala推理:1次评估失败,中间值成功

Yan*_*gne 2 scala type-inference

我是scala的初学者,不明白这里发生了什么:

鉴于:

val reverse:Option[MyObject] = ...
Run Code Online (Sandbox Code Playgroud)

myObject.isNaire返回布尔值.

如果我做 :

val v:Option[Boolean] =  reverse.map(_.isNaire)
val b:Boolean = v.getOrElse(false)
Run Code Online (Sandbox Code Playgroud)

这行得通.

现在,如果我这样做:

val b:Boolean = reverse.map(_.isNaire).getOrElse(false)
Run Code Online (Sandbox Code Playgroud)

它无法编译 type mismatch: found Any, required Boolean


编辑:谢谢Beryllium,通过制作SSCCE,我找到了解释的开始.在第一个示例中,myObject是一个java类,因此isNaire是一个java.lang.Boolean.我认为隐式转换应该使这个透明,所以解释仍然是受欢迎的.

class Test(val naire:java.lang.Boolean)

class Other {
  val testValue = Some(new Test(true))
  def mysteriousCompilationError:Boolean = testValue.map(_.naire).getOrElse(false)
}
Run Code Online (Sandbox Code Playgroud)

注意:ScalaCompiler是2.10.2

Noa*_*oah 5

scala.Predef有从隐式转换java.lang.Booleanscala.Boolean:

  implicit def Boolean2boolean(x: java.lang.Boolean): Boolean = x.booleanValue
Run Code Online (Sandbox Code Playgroud)

因此,在第一种情况下val v:Option[Boolean] = reverse.map(_.isNaire),编译器会看到a java.lang.Boolean并在范围内查找隐式方法,将其转换为a scala.Boolean,它可以方便地找到它scala.Predef.

在第二种情况下,testValue.map(_.naire).getOrElse(false)编译器按此顺序执行操作:

  1. Option[Test] => Option[java.lang.Boolean]
  2. getOrElse[B >: A](default: => B): B这里Ajava.lang.BooleanBAny因为scala.Boolean>: java.lang.Boolean
  3. val b:Boolean,编译器不能找到一个隐式转换Anyscala.Boolean

要解决这个问题的唯一办法,就是在地图操作过程中告诉编译器使用隐式转换,从scala.Predef从去java.lang.Booleanscala.Boolean:

def works:Boolean = testValue.map[Boolean](_.naire).getOrElse(false)
Run Code Online (Sandbox Code Playgroud)

这是一个常见问题,经常出现,因为map其后getOrElse非常便利.要在没有额外类型的情况下正确修复此问题,请fold对选项使用(catamorphism):

def worksToo:Boolean = testValue.fold(false)(_.naire)
Run Code Online (Sandbox Code Playgroud)

通过使用,fold您可以获得一些额外的类型安全性,因为没有转换到常见类型.例如,你不能这样做:

def failsTypeCheck = testValue.fold("test")(_.naire)
Run Code Online (Sandbox Code Playgroud)

虽然编译器对此没有问题:

def passesTypeCheck = testValue.map(_.naire).getOrElse("test")
Run Code Online (Sandbox Code Playgroud)