考虑一下由类型构造函数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
的形状不正确。
现在,让我们添加一个从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
这将进行编译,但是请注意,隐式转换似乎并未实际应用,因为res1
的运行时类是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)
发布于 2020-06-17 10:31:03
我以前注意到过这个问题,我认为它可以追溯到编译器中的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])
将为false,并且methTypeArgs
调用将抛出DeferredNoInstance
异常-它甚至不会将Any
作为F
的候选对象。
如果您有隐式转换,isCompatible(Bar, F[A])
就是真的,但是编译器很快就会忘记它只是因为隐式转换才是真的,并且solvedTypes
为F
选择Any
,这是允许的,因为Scala为Any
提供了奇怪的特殊情况下的种类多态性。在这一点上,Bar
值是完全正确的,因为它是一个Any
,并且编译器不会应用它在isCompatible
中找到的转换(它忘记了它需要转换)。
(注意,如果您为f
提供了显式的类型参数,则根本不会调用methTypeArgs
,并且isCompatible
和solvedTypes
之间的这种差异是无关紧要的。)
我想这一定是编译器中的一个bug。我不知道是否有人已经上报了(我只是花了五分钟的时间看了一下,没有看到)。
https://stackoverflow.com/questions/62415172
复制相似问题