Scala 隐式搜索协变类型 class 将类型参数替换为 Nothing。为什么?
Scala implicit search of covariant type class replaces the type argument with Nothing. Why?
让我们用一个现实世界的例子。一种字符串解析器类型 class,其隐式实例由将创建委托给工厂的函数创建。
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("")
}
}
工厂接收到的类型参数T
在Parser
非变体时为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 Mitin给出的答案和下面有用的评论,翻译成我自己的话和有限的思考范围,供以后参考。
让我无法理解的是错误的想法,即当隐式值提供程序是具有参数化结果类型的 def
时,没有一组活值可供编译器从中选择一个。在那种情况下,我想,它只是跳过了那一步(选择具有最具体声明类型的值的那一步)。
鉴于 summoner 函数授予编译器构建任何类型值的权力,为什么不用让他高兴的值填充隐式参数。如果隐式参数需要一些可分配给类型 T
的东西,那么给它一个类型 T
的值。给它 Nothing
,它可以分配给所有东西,既不好也没有用。
当有多个召唤者提供可分配给隐式参数类型的值时,该想法就会出现问题。在那种情况下,决定选择哪个召唤者的唯一一致的方法是推断出它们产生的值的类型集,根据既定标准(例如,最具体的)从所述集合中选择一个类型,然后选择生产它的召唤师。
Scala 规范说
If there are several eligible arguments which match the implicit parameter's type, a most specific one will be chosen using the rules of static overloading resolution
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
) 都是符合条件的候选者,因此编译器会选择最具体的一个。
让我们用一个现实世界的例子。一种字符串解析器类型 class,其隐式实例由将创建委托给工厂的函数创建。
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("")
}
}
工厂接收到的类型参数T
在Parser
非变体时为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 Mitin给出的答案和下面有用的评论,翻译成我自己的话和有限的思考范围,供以后参考。
让我无法理解的是错误的想法,即当隐式值提供程序是具有参数化结果类型的 def
时,没有一组活值可供编译器从中选择一个。在那种情况下,我想,它只是跳过了那一步(选择具有最具体声明类型的值的那一步)。
鉴于 summoner 函数授予编译器构建任何类型值的权力,为什么不用让他高兴的值填充隐式参数。如果隐式参数需要一些可分配给类型 T
的东西,那么给它一个类型 T
的值。给它 Nothing
,它可以分配给所有东西,既不好也没有用。
当有多个召唤者提供可分配给隐式参数类型的值时,该想法就会出现问题。在那种情况下,决定选择哪个召唤者的唯一一致的方法是推断出它们产生的值的类型集,根据既定标准(例如,最具体的)从所述集合中选择一个类型,然后选择生产它的召唤师。
Scala 规范说
If there are several eligible arguments which match the implicit parameter's type, a most specific one will be chosen using the rules of static overloading resolution
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
) 都是符合条件的候选者,因此编译器会选择最具体的一个。