Scala:使用隐式证据的通用方法';不编译
我通过练习《不耐烦的人的Scala》一书中的练习来学习Scala。一个问题是: 给定一个可变的Scala:使用隐式证据的通用方法';不编译,scala,generics,types,implicit-conversion,Scala,Generics,Types,Implicit Conversion,我通过练习《不耐烦的人的Scala》一书中的练习来学习Scala。一个问题是: 给定一个可变的对[S,T]类,使用类型约束来定义 如果类型参数相同,则可以调用的swap方法 我的代码: class Pair[T, S](var first: T, var second: S) { def swap(implicit ev: T =:= S) { val temp = first first = second // doesn't compile second = te
对[S,T]
类,使用类型约束来定义
如果类型参数相同,则可以调用的swap方法
我的代码:
class Pair[T, S](var first: T, var second: S) {
def swap(implicit ev: T =:= S) {
val temp = first
first = second // doesn't compile
second = temp
}
}
上述代码未能编译,抱怨
first
和second
是不同的类型。嗯,我只是很好地告诉编译器它们不是。我怎么能告诉它闭嘴呢?你刚刚告诉编译器,作为T
和S
传递给你的类的类型应该是相等的-你只需要它们相等的证据,当你传递实际的类型时(而不是在泛型类本身内部),它们可以用来正确地推断实际的T
和S
。这并不意味着t
和S
是可交换的。顺便说一句,它不会改变任何东西,但是您犯了一个错误,定义了新的S
和t
,应该是:
class Pair[T, S](var first: T, var second: S) {
def swap(implicit ev: T =:= S) { //without [S, T] - they are unrelated to original ones, it's whole new names
val temp = first
first = second // doesn't compile
second = temp
}
}
但是它仍然没有编译。为什么?想想看:
def getBoth(implicit ev: T =:= S) = List(first, second)
那么编译器应该推断什么是返回类型呢<代码>列表[T]或列表[S]
。它唯一能做的就是列出[任何]。因此,同时拥有相同和不同的类型是没有意义的。如果您想要不同的名称,只需使用type S=T
即可,无需任何证据
什么是真正的情况,有
S
和T
在构造函数上不同,在某些方法上相同。这在逻辑上是不正确的(当谈论抽象类型时,而不是具体的替换)
顺便说一句,=:=
不是编译器的特性-编译器中没有针对该特性的特殊处理。整个实现是在scalaPredef
中实现的:
@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]
}
所以它只是tpEquals[A]
隐式的,它接受类型A
,并给你A=:=A
——当你需要T=:=U
时,它试图进行这样的转换,这种转换只适用于相同的类型。而检查隐式本身的过程实际上只有在传递实际的T
和U
时才会发生,而不是在定义它们时
关于您的特定问题:
class Pair[T, S](var first: T, var second: S) {
def swap(implicit ev: T =:= S) {
val temp = first
first = second.asInstanceOf[T]
second = temp.asInstanceOf[S]
}
}
scala> new Pair(5,6)
res9: Pair[Int,Int] = Pair@6bfc12c4
scala> res9.swap
scala> res9.first
res11: Int = 6
或者(正如@m-z和@Imm所建议的那样):
T=:=S
扩展了T=>S
,这个隐式添加的函数(甚至作为一个对象)在Scala中被解释为从T
到S
的隐式转换,因此它的工作原理就像两种类型是相等的,这很酷。您是如何解决给定的问题的?从问题陈述“给定一个可变对[S,T]类…”中,我猜作者不希望我在类级别上定义隐式证据,而只在方法级别上定义隐式证据。当您可以调用ev(first)
等时,为什么要强制转换?@dk14您可以通过同时要求T=:=S
和S=:=T
来解决这个问题。真正的情况是,S和T在构造函数(!)中不同,在某些方法中相同。这在逻辑上是不正确的。“我认为这是不对的。它的基本意思是:好的,编译器,让Pair
有任何类型的字段,但是如果它们恰好相等,那么让它也有一个方法swap
。因为所有这些都是编译时的,所以不会编译错误的用法。它甚至没有破坏任何OOP保证。以Seq.toMap
的定义为例:toMap[T,U](隐式ev:@Abhijit Sarkar正如我首先所说的,=:=
不是一个编译器特性,它是基于隐式的技巧。T=:=s
和s=:=T
与外部世界平等的观点相同,但不是在方法内部。因此,您需要将s转换为T
(first=second
)和T到S
(second=temp
),因为它们对于编译器来说不是相同的类型。因此T=:=S
是函数T=>S
的一个实例,这有助于做到这一点(并避免成为的实例)因此T=:=S
也起到了从T
到S
的隐式转换的作用(实际上,是内部的替代)
class Pair[T, S](var first: T, var second: S) {
def swap(implicit ev: T =:= S, ev2: S =:= T) {
val temp = first
first = second
second = temp
}
}