Scala 类型证据 [英] Scala type evidences

查看:47
本文介绍了Scala 类型证据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 Scala 源码中我可以看到这段代码:

@implicitNotFound(msg = "无法证明 ${From} <:< ${To}.")密封抽象类 <:<[-From, +To] extends (From => To) with Serializableprivate[this] 最终 val singleton_<:<= new <:<[Any,Any] { def apply(x: Any): Any = x }//不在 <:<伴随对象,因为它也是//旨在包含身份(不再隐含)隐式定义符合 [A]: A <: To) with Serializableprivate[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x }对象 =:= {隐式 def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A]}

我不太清楚,为什么这两个类都扩展了 Function1.扩展任何东西都不够吗?

大多数时候,当您想确保 A <:<B(或者更准确地说,您要确保 A <: B 使用 A <: 类型的证据),它是因为您实际上有一个 A 类型的值,并且希望能够将其视为 B 类型的实例.

当您有一个值 x: A 时,通过隐式值的存在证明 AB 的子类型不会神奇地将 x 的类型更改为 B.

但出于所有意图和目的 <:< 实际上允许这样做,因为它也是一个仅返回其参数的函数(它基本上是身份,只是添加了一个 - 并且稍微隐藏了- 投).这样,当您的方法传递一个 A <:< 类型的隐式值时B,你得到的其实也是一个合适的从 AB 的隐式转换(隐式转换 x:A 到一个值类型B).

如果您实际上不需要转换任何内容,那么 <:< 是否扩展 Function1 并不重要.

同样的原理适用于 =:=.

更新:响应在 =:= 的情况下,为什么我要将 A 类型的值转换为 A 类型的值?":

首先你应该注意到,即使在 <:< 的情况下,也存在同样明显的矛盾:当然如果 A <:B 我可以处理任何A 类型的值作为 B 类型的值(这几乎是子类型的定义).假设我们有以下通用方法:

class Foo {def hello() { println("你好!") }}def f[T]( value: T )(隐式 e: T <:

在编译f 时,编译器只知道value 具有某种类型T.没有什么可以告诉编译器 T 将始终是 Foo 的子类型.所以如果不是因为 e: T <:<Foo 还提供了从 TFoo 的隐式转换,那么调用 value.hello() 就会失败,因为 T 只是编译器不知道的某种类型任何关于.只有经过精心设计,才具有隐含值 e: T <:<;Foo 在作用域内发生当且仅当 T <: Foo.但是编译器对此一无所知,因此在他看来TFoo 是无关的.因此,我们必须为他提供一种将 T 类型的值转换为 Foo 的方法,这是由 T <:< 完成的.Foo 证据本身.

正如我所说,同样的原理也适用于 =:=:拥有一个 T =:= Foo 的实例不会让编译器知道这个事实T = Foo ,因此必须向他提供转换.

In scala source I can see this code:

@implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.")
sealed abstract class <:<[-From, +To] extends (From => To) with Serializable
private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
// not in the <:< companion object because it is also
// intended to subsume identity (which is no longer implicit)
implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]

@implicitNotFound(msg = "Cannot prove that ${From} =:= ${To}.")
sealed abstract class =:=[From, To] extends (From => To) with Serializable
private[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x }
object =:= {
    implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A]
}

It's not entirely clear to me, why both these classes extend Function1. Wouldn't extending anything suffice?

Most of the time when you want to ensure that A <:< B (or more correctly, you want to ensure that A <: B using an evidence of type A <:< B), it is because you actually have a value of type A and want to be able to treat it as an instance of type B.

When you have a value x: A, proving via the presence of an implicit value that A is a sub-type of B won't magically change the type of x to B.

But for all intents and purpose <:< actually allows this because it is also a function that just returns its argument (it is basically the identity, with just an added - and slightly hidden - cast). This way when your method get passed an implicit value of type A <:< B, what you get is actually also a suitable implicit conversion from A to B (implicitly converting x:A to a value of type B).

In the case when you don't actually need to convert anything, well that does not really matter whether <:< extends Function1 or not.

The same rationale applies to =:=.

UPDATE: In response to "In the case of =:=, Why would I want to convert values of type A to value of type A ?":

You should first note that even in the case of <:< there is the same apparent contradiction: surely if A <: B I can treat any value of type A as a value of type B (this is pretty much the definition of sub-typing). Say we have the following generic method:

class Foo { 
  def hello() { println("hello!") } 
}
def f[T]( value: T )(implicit e: T <:< Foo){
  value.hello()
}
class Bar extends Foo
f( new Bar )

When compiling f the compiler only knows that value has some type T. Nothing tells the compiler that T will always be a sub-type of Foo. So if it were not for the fact that e: T <:< Foo also provides an implicit conversion from T to Foo, then the call value.hello() would fail because T is just some type that the compiler does not know anything about. It is only by careful design that having an implicit value e: T <:< Foo in scope happens if and only if T <: Foo. But the compiler has no idea of this, so from his point of view T and Foo are unrelated. Thus we have to provide him a way to convert values of type T to Foo, which is done by the T <:< Foo evidence itself.

As I said, the same rationale applies to =:=: having an instance of T =:= Foo gives no clue to the compiler about the fact that T = Foo , so the conversion must be provided to him.

这篇关于Scala 类型证据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆