协变类型类的 Scala 隐式搜索将类型参数替换为 Nothing.为什么?



让我们用一个真实世界的例子。一个字符串分析器类型类,其隐式实例由将创建委托给工厂的函数创建。

import scala.reflect.runtime.universe.TypeTag
object Test {
trait Parser[+T] { def parse(input: String): T }
implicit def summonParserOf[T](implicit factory: ParserFactory[T]): Parser[T] = factory.build
trait ParserFactory[T] { def build: Parser[T] }
implicit def summonFactoryOfParsersOf[T](implicit t: TypeTag[T]): ParserFactory[T] =
new ParserFactory[T] {
def build: Parser[T] = new Parser[T] {
def parse(input: String) = {
println("T = " + t.tpe) // this outputs "T = Int" if Parser is non variant, and "T = Nothing" if Parser is covariant on T. Why?
null.asInstanceOf[T]
}
}
}
def main(args: Array[String]): Unit = {
val parserOfInt = implicitly[Parser[Int]]
parserOfInt.parse("")
}
}

工厂接收的类型参数TParser为非变量时Int,在协变时Nothing。为什么?

编辑1:不需要工厂。替换发生在之前。因此,测试可以简化为:

package jsfacile.test
import scala.reflect.runtime.universe.TypeTag
object Probando {
trait Parser[+T] { def parse(input: String): T }
implicit def summonParserOf[T](implicit t: TypeTag[T]): Parser[T] = new Parser[T] {
def parse(input: String): T = {
println("summon parser: T = " + t.tpe) // this outputs "T = Int" if Parser is non variant, and "T = Nothing" if Parser is covariant on T. Why?
null.asInstanceOf[T]
null.asInstanceOf[T]
}
}
def main(args: Array[String]): Unit = {
val parserOfInt = implicitly[Parser[Int]]
parserOfInt.parse("")
}
}

Parser[Nothing]可以分配给Parser[Int],但选择下限而不是上限的目的是什么?

编辑2:米廷@Dmytro给出的答案和下面的有用评论,翻译成我自己的话和有限的思维范围,供以后参考我自己。

阻止我理解的是错误的想法,即当隐式值提供程序是具有参数化结果类型的def时,编译器必须从中选择一组活值。在这种情况下,我想,它只是跳过该步骤(选择具有最具体声明类型的值的步骤)。 鉴于 summoner 函数授予编译器构建任何类型的值的权力,为什么不用让他满意的值填充隐式参数呢?如果隐式参数需要可分配给类型T的东西,则为其指定一个类型为T的值。给它Nothing,这是可以分配给一切的,既不好,也没有用。

当有多个召唤师提供可分配给隐式参数类型的值时,就会出现这个想法的问题。在这种情况下,决定选择哪个召唤师的唯一一致方法是推断他们产生的值的类型集,根据既定标准(例如最具体的标准)从所述集合中选择一个类型,然后选择产生它的召唤师。

Scala 规范说

如果有多个符合条件的参数与隐式参数的类型匹配,则将使用静态重载解析规则选择一个最具体的参数

https://scala-lang.org/files/archive/spec/2.11/07-implicits.html#implicit-parameters

由于您定义了诸如

implicit def summonParserOf[T](implicit t: TypeTag[T]): Parser[T] = ...

对于协变

trait Parser[+T] { ... }

当你寻找implicitly[Parser[T]]时,所有summonParserOf[S](S <: T)都是合格的候选者,所以编译器选择最具体的一个。

最新更新