Scala隐式转换在某些条件下应用,但在其他条件下不适用

eje*_*eje 7 scala implicit compiler-bug implicit-conversion implicits

这是一个简单的重现器,我在其中定义了一个带有隐式重新排序转换的"可交换"对类型.如果函数的参数f位于预先存在的命名值(t在示例中),则编译器会按预期应用隐式转换.但是,如果我尝试f直接调用literal CommutativePair,它会因类型错误而失败.在这种情况下,编译器不应用隐式重新排序转换.

object repro {
  import scala.language.implicitConversions

  case class CommutativePair[A, B](a: A, b: B)

  object CommutativePair {
    // Support a kind of commutative behavior via an implicit reordering
    implicit def reorderPair[B, A](pair: CommutativePair[B, A]) =
      CommutativePair(pair.b, pair.a)
  }

  // The idea is to allow a call to 'f' with Pair[Int, String] as well,
  // via implicit reorder.
  def f(p: CommutativePair[String, Int]) = p.toString

  val t = CommutativePair(3, "c")

  // This works: the implicit reordering is applied
  val r1 = f(t)

  // This fails to compile: the implicit reordering is ignored by the compiler
  val r2 = f(CommutativePair(3, "c"))
}
Run Code Online (Sandbox Code Playgroud)

Alv*_*sco 2

我相信它已经达到了 scala 推理的极限,这是由它搜索解决方案的顺序触发的。第一种情况:

val t = CommutativePair(3, "c")
Run Code Online (Sandbox Code Playgroud)

推理已将类型锁定为CommutativePair[Int,String],因为它是唯一可以根据参数工作的类型。所以当它调用时:

val r1 = f(t)
Run Code Online (Sandbox Code Playgroud)

Commutative[Int,String]它在=!=上得到类型不匹配Commutative[String,Int],然后它搜索隐式并找到上面的隐式。

在第二种情况下,scala 试图从外部找出类型:

val r2 = f(CommutativePair(3, "c"))
Run Code Online (Sandbox Code Playgroud)
  • 首先,它决定f,必须采取Commutative[String,Int]
  • 然后,它CommutativePair(...,...) 必须Commutative[String,Int](因为它还没有从它的参数中找出它的类型)。
  • 现在它查看参数CommutativePair(...)并发现它们的类型错误。但这不会触发隐式转换,因为它认为不匹配是在参数中,而不是在整体上CommutativePair(...)

事实上,锁定类型参数(通过显式传递它们或首先绑定到 val)可以修复错误。