<:<如何?运营商在Scala工作?

时间:2015-06-02 21:16:55

标签: scala type-constraints

在Scala中,有一个类<:<见证了类型约束。来自Predef.scala

  sealed abstract class <:<[-From, +To] extends (From => To) with Serializable
  private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
  implicit def $conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]

toMap TraversableOnce def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = 方法中使用的示例:

A <:< B

我不明白这是如何运作的。我理解<:<[A, B]在语法上等同于A <: B类型。但是,当且仅当asInstanceOf时,我不知道编译器如何找到隐含的类型。我假设$conforms定义中的object调用使某种方式成为可能,但是如何?此外,使用抽象类的单例实例而不仅仅使用{{1}}是否很重要?

1 个答案:

答案 0 :(得分:12)

假设我们有以下简单类型层次结构:

trait Foo
trait Bar extends Foo

我们可以要求Bar延伸Foo

的证明
val ev = implicitly[Bar <:< Foo]

如果我们在-Xprint:typer的控制台中运行此功能,我们会看到以下内容:

private[this] val ev: <:<[Bar,Foo] =
  scala.this.Predef.implicitly[<:<[Bar,Foo]](scala.this.Predef.$conforms[Bar]);

所以编译器选择$conforms[Bar]作为我们要求的隐含值。当然,此值的类型为Bar <:< Bar,但由于<:<在其第二个类型参数中是协变的,因此它是Bar <:< Foo的子类型,因此它适合帐单。

(这里有一些神奇的事实,Scala编译器知道如何查找它正在寻找的类型的子类型,但它是一个相当通用的机制,并且在其行为方面并不太令人惊讶。)

现在假设我们要求提供Bar扩展String

的证明
val ev = implicitly[Bar <:< String]

如果您开启-Xlog-implicits,您会看到:

<console>:9: $conforms is not a valid implicit value for <:<[Bar,String] because:
hasMatchingSymbol reported error: type mismatch;
 found   : <:<[Bar,Bar]
 required: <:<[Bar,String]
       val ev = implicitly[Bar <:< String]
                          ^
<console>:9: error: Cannot prove that Bar <:< String.
       val ev = implicitly[Bar <:< String]
                          ^

编译器再次尝试Bar <:< Bar,但由于Bar不是String,因此这不是Bar <:< String的子类型,所以它不是我们需要的。但是$conforms是编译器可以获得<:<实例的唯一地方(除非我们已经定义了自己的实例,这将是危险的),所以它非常正确地拒绝编译这些废话。

要解决第二个问题:<:<[-From, +To]类是必需的,因为我们需要此类型类的类型参数才有用。单例Any <:< Any值也可以定义为对象 - 使用val和匿名类的决定可以说有点简单,但它是一个你不应该需要的实现细节担心。