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

我通过练习《不耐烦的人的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 = 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
在构造函数上不同,在某些方法上相同。这在逻辑上是不正确的(当谈论抽象类型时,而不是具体的替换)

顺便说一句,
=:=
不是编译器的特性-编译器中没有针对该特性的特殊处理。整个实现是在scala
Predef
中实现的:

@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
    }
}