仅仅存在隐式转换就可以使程序在从未被应用的情况下编译
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
形状不正确。
现在让我们添加一个从 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
的运行时 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= 之间存在这种差异] 无关紧要。)
我认为这一定是编译器中的错误。不知道有没有人反映过(我才看了五分钟,没看到)。
考虑由类型构造函数 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
的运行时 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= 之间存在这种差异] 无关紧要。)
我认为这一定是编译器中的错误。不知道有没有人反映过(我才看了五分钟,没看到)。