问题描述
在Scala中,有一个类<:<
见证了类型约束.来自Predef.scala
:
In Scala there's a class <:<
that witnesses a type constraint. From 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]
如何使用它的一个例子是TraversableOnce
的toMap
方法:
An example of how it's used is in the toMap
method of TraversableOnce
:
def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] =
我不了解的是这是如何工作的.我了解A <:< B
在语法上等同于类型<:<[A, B]
.但是我不知道,当且仅当A <: B
时,编译器如何才能找到该类型的隐式.我认为$conforms
定义中的asInstanceOf
调用使这成为可能,但是如何?另外,使用抽象类的单例实例而不是仅使用object
是否有意义?
What I don't understand is how this works. I understand that A <:< B
is syntactically equivalent to the type <:<[A, B]
. But I don't get how the compiler can find an implicit of that type if and only if A <: B
. I assume that the asInstanceOf
call in the definition of $conforms
is making this possible somehow, but how? Also, is it significant that a singleton instance of an abstract class is used, instead of just using an object
?
推荐答案
假设我们有以下简单的类型层次结构:
Suppose we've got the following simple type hierarchy:
trait Foo
trait Bar extends Foo
我们可以要求证明Bar
扩展了Foo
:
We can ask for proof that Bar
extends Foo
:
val ev = implicitly[Bar <:< Foo]
如果我们使用-Xprint:typer
在控制台中运行此命令,则会看到以下内容:
If we run this in a console with -Xprint:typer
, we'll see the following:
private[this] val ev: <:<[Bar,Foo] =
scala.this.Predef.implicitly[<:<[Bar,Foo]](scala.this.Predef.$conforms[Bar]);
因此,编译器选择了$conforms[Bar]
作为我们要求的隐式值.当然,该值的类型为Bar <:< Bar
,但是由于<:<
在其第二个类型参数中是协变的,因此它是Bar <:< Foo
的子类型,因此非常适合.
So the compiler has picked $conforms[Bar]
as the implicit value we've asked for. Of course this value has type Bar <:< Bar
, but because <:<
is covariant in its second type parameter, this is a subtype of Bar <:< Foo
, so it fits the bill.
(Scala编译器知道如何查找要查找的类型的子类型,这有些神奇,但这是一种相当通用的机制,其行为也并不令人惊讶.)
(There's some magic involved here in the fact that the Scala compiler knows how to find subtypes of the type it's looking for, but it's a fairly generic mechanism and isn't too surprising in its behavior.)
现在假设我们要求提供证明Bar
扩展了String
的证据:
Now suppose we ask for proof that Bar
extends String
:
val ev = implicitly[Bar <:< String]
如果打开-Xlog-implicits
,则会看到以下内容:
If you turn on -Xlog-implicits
, you'll see this:
<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
不是String
,因此它不是Bar <:< String
的子类型,因此这不是我们所需要的.但是$conforms
是编译器可以获取<:<
实例的唯一位置(除非我们定义了自己的实例,否则将很危险),因此它相当正确地拒绝编译此废话.
The compiler tries the Bar <:< Bar
again, but since Bar
isn't a String
, this isn't a subtype of Bar <:< String
, so it's not what we need. But $conforms
is the only place the compiler can get <:<
instances (unless we've defined our own, which would be dangerous), so it quite properly refuses to compile this nonsense.
要解决您的第二个问题:<:<[-From, +To]
类是必需的,因为我们需要该类型类的类型参数才有用.单例Any <:< Any
值也可以定义为对象-使用val
和匿名类的决定可以说简单一些,但这是您无需担心的实现细节.
To address your second question: the <:<[-From, +To]
class is necessary because we need the type parameters for this type class to be useful. The singleton Any <:< Any
value could just as well be defined as an object—the decision to use a val
and an anonymous class is arguably a little simpler, but it's an implementation detail that you shouldn't ever need to worry about.
这篇关于<:<运营商在Scala中工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!