仅仅存在隐式转换就可以使程序在从未被应用的情况下编译

Mere presence of implicit conversion makes the program compile despite never being applied

考虑由类型构造函数 F[_] 和适当类型 A

参数化的方法 f
def f[F[_], A](v: F[A]) = v

让我们尝试将其应用到 new Bar

scala> class Bar
class Bar

scala> def f[F[_], A](v: F[A]) = v
def f[F[_], A](v: F[A]): F[A]

scala> f(new Bar)
       ^
       error: no type parameters for method f: (v: F[A]): F[A] exist so that it can be applied to arguments (Bar)
        --- because ---
       argument expression's type is not compatible with formal parameter type;
        found   : Bar
        required: ?F[?A]
         ^
       error: type mismatch;
        found   : Bar
        required: F[A]

此错误符合预期,因为 Bar 形状不正确。

现在让我们添加一个从 BarList[Int]

的隐式转换
scala> implicit def barToList(b: Bar): List[Int] = List(42)
def barToList(b: Bar): List[Int]

scala> f(new Bar)
val res1: Any = Bar@56881196

这可以编译,但是请注意隐式转换似乎并没有实际应用,因为 res1 的运行时 class 是 Bar 而不是 List。此外,res1 的编译时类型是 Any 而不是 List[Int]。查看 -Xprint:typer 的输出,我们看到类似

的内容
val res1: Any = f[Any, Nothing](new Bar())

我们看到以下推论发生的地方

F[_] = Any
A = Nothing 

相对于

F[_] = List
A = Int 

我们看到实际上没有发生任何转换,也就是说,我们没有看到

f(barToList(new Bar()))

为什么仅仅存在隐式转换就可以编译程序,而实际上没有应用隐式转换?请注意,当明确说明类型参数时,它会按预期工作

scala> f[List, Int](new Bar)
val res2: List[Int] = List(42)

我之前注意到这个问题,我认为它可以在编译器中追踪到 this code

  // Then define remaining type variables from argument types.
  foreach2(argtpes, formals) { (argtpe, formal) =>
    val tp1 = argtpe.deconst.instantiateTypeParams(tparams, tvars)
    val pt1 = formal.instantiateTypeParams(tparams, tvars)

    // Note that isCompatible side-effects: subtype checks involving typevars
    // are recorded in the typevar's bounds (see TypeConstraint)
    if (!isCompatible(tp1, pt1)) {
      throw new DeferredNoInstance(() =>
        "argument expression's type is not compatible with formal parameter type" + foundReqMsg(tp1, pt1))
    }
  }
  val targs = solvedTypes(tvars, tparams, varianceInTypes(formals), upper = false, lubDepth(formals) max lubDepth(argtpes))

据我了解,问题是 isCompatible 检查寻找隐式转换,但不跟踪是否需要,而 solvedType 只看边界.

因此,如果您没有隐式转换,isCompatible(Bar, F[A]) 为假并且 methTypeArgs 调用会抛出 DeferredNoInstance 异常——它甚至不会考虑 Any 作为 F.

的候选人

如果您确实进行了隐式转换,isCompatible(Bar, F[A]) 为真,但编译器会立即忘记它只是因为隐式转换才为真,并且 solvedTypes 选择 Any 作为 F,这是允许的,因为 Scala 对 Any 的奇怪特例种类多态性。在这一点上,Bar 值非常好,因为它是一个 Any,并且编译器不应用它在 isCompatible 中找到的转换(它忘记了它需要)。

(附带说明,如果您为 f 提供显式类型参数,则根本不会调用 methTypeArgs,并且 isCompatible 和 [=19= 之间存在这种差异] 无关紧要。)

我认为这一定是编译器中的错误。不知道有没有人反映过(我才看了五分钟,没看到)。