Mar*_*lic 10 scala type-inference implicit-conversion type-parameter type-constructor
考虑f由类型构造函数F[_]和适当类型参数化的方法A
def f[F[_], A](v: F[A]) = v
Run Code Online (Sandbox Code Playgroud)
让我们尝试将其应用于 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]
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,此错误Bar的形状不正确。
现在让我们添加一个从Bar到的隐式转换List[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
Run Code Online (Sandbox Code Playgroud)
这可以编译,但是请注意,实际上似乎并未应用隐式转换,因为res1isBar和 not的运行时类List。此外, 的编译时类型res1isAny和 not List[Int]。查看-Xprint:typer我们的输出,我们看到类似
val res1: Any = f[Any, Nothing](new Bar())
Run Code Online (Sandbox Code Playgroud)
我们看到以下推理发生的地方
F[_] = Any
A = Nothing
Run Code Online (Sandbox Code Playgroud)
与
F[_] = List
A = Int
Run Code Online (Sandbox Code Playgroud)
我们看到实际上没有发生任何转换,也就是说,我们没有看到类似的东西
f(barToList(new Bar()))
Run Code Online (Sandbox Code Playgroud)
为什么仅仅存在隐式转换就可以编译程序,而实际上没有应用隐式转换?请注意,当明确类型参数时,它会按预期工作
scala> f[List, Int](new Bar)
val res2: List[Int] = List(42)
Run Code Online (Sandbox Code Playgroud)
Tra*_*own 10
我以前注意到这个问题,我认为它可以追溯到编译器中的这段代码:
// 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))
Run Code Online (Sandbox Code Playgroud)
据我了解,问题在于isCompatible检查会寻找隐式转换,但不会跟踪是否需要转换,而solvedType只是查看边界。
因此,如果您没有隐式转换,isCompatible(Bar, F[A])则为 false 并且methTypeArgs调用会抛出DeferredNoInstance异常——它甚至不会被Any视为F.
如果您确实有隐式转换,isCompatible(Bar, F[A])则为真,但编译器会立即忘记它只是因为隐式转换才为真,并solvedTypes选择Anyfor F,这是允许的,因为 Scala 奇怪的特殊情况 for 种类多态性Any。那时该Bar值非常好,因为它是一个Any,并且编译器不会应用它在isCompatible其中找到的转换(它忘记了它需要)。
(作为一个方面说明,如果你提供明确的类型参数f,methTypeArgs完全不叫之间,以及这种差异isCompatible与solvedTypes无关。)
我认为这一定是编译器中的错误。不知道有没有人举报过(我只是看了五分钟没看到)。