为什么在对Option.getOrElse的返回值使用隐式转换时Scala中的类型推断失败?

时间:2017-03-24 07:39:06

标签: scala

例如,我有一个类 Value 和一个隐式函数将字符串转换为Value:

case class Value(v: String)

implicit def strToValue(s: String): Value = Value(s)

这是一个具有方法返回值的特征:

trait ReturnValue {
  def f: Value
}

因为隐式转换存在,我只需返回一个字符串文字即可实现方法 f

object Obj1 extends ReturnValue {
  override def f: Value = "123"
}

当然返回一个String类型的变量就可以了:

object Obj2 extends ReturnValue {
  override def f: Value = {
    val v = Some("123").getOrElse("234")
    v
  }
}

但是当我尝试直接使用Option.getOrElse的结果作为返回值时:

object Obj3 extends ReturnValue {
  override def f: Value = Some("123").getOrElse("234")  // Compilation error: type mismatch
}

发生编译错误:

Error:(23, 50) type mismatch;
 found   : java.io.Serializable
 required: Entry.Value
   override def f: Value = Some("123").getOrElse("234") // Compilation error: type mismatch

这里的类型推断似乎失败了。未推断出类型 String ,然后无法匹配隐式转换。 (完整档案为here

我尝试过其他具有类型参数的函数,例如“map”,它们都能很好地工作。

为什么Option.getOrElse如此特殊以至于类型推断在这里失败了?

2 个答案:

答案 0 :(得分:2)

此变体导致相同的编译错误,并可能显示编译器如何解构表达式:

object Obj3 extends ReturnValue {
  override def f: Value = {
    val v = Some("123") // is of type Some(String)
    v.getOrElse("234": Value)  // Compilation error: type mismatch
  }
}

同样的错误也可以在没有任何特征的情况下实现,具有以下简单的重复:

case class Value(v: String)

implicit def strToValue(s: String): Value = Value(s)

val vs  = Some("123")

val v: Value = vs.getOrElse("234")

似乎编译器将Value的转换应用于getOrElse的参数,而不是其结果。事实是可以通过启用scalacOptions in Compile ++= Seq("-Xprint-types", "-Xprint:typer")的输出来确认(清理了一点 - 删除了明显不相关的注释):

  

private [this] val v:Value =

     

vs.getOrElse {[B>:String](默认值:=> B)B} [java.io.Serializable] {(默认值:=> java.io.Serializable)java.io.Serializable} ( strToValue {(s:String)Value}(" 234" {String(" 234")}){Value} ){

我认为推论的工作原理如下:

  • vs类型称为Some[String]
  • getOrElse声明为def getOrElse[B >: A](default: => B): B(此处AString
  • 编译器将B推断为Value,因为这是表达式的预期结果类型。
  • ValueString最具体的超类型为Serializable

您还可以注意完全删除隐式转换时的行为方式。 val v: Value = vs.getOrElse("234")的错误是:类型不匹配;  发现:字符串(" 234")  必需:值

答案 1 :(得分:0)

这似乎是一个编译器错误。这是一个显示奇怪行为的例子。

case class ValString(string: String)
object ValString {
  implicit def string2ValString(string: String): ValString = ValString(string)
}

case class ValThing( one: ValString, two: ValString, three: ValString, four: ValString, five: ValString)

val opt = Option("aString")
val x = ValThing(
  if(opt.isEmpty) "" else opt.get,
  opt.fold("")(identity),
  opt.orElse(Option("")).get,
  opt.getOrElse(""): String,
  ValString(opt.getOrElse(""))
)

以上都可以,但

val x = ValThing( opt.getOrElse(""), .... )

失败,因为它将getOrElse的输出解释为Serializable