Scala:使用隐式证据的通用方法无法编译

Scala: generic method using implicit evidence doesn't compile

我正在通过书中的练习来学习 Scala "Scala for the Impatient"。一题问:

Given a mutable Pair[S, T] class, use a type constraint to define a swap method that can be called if the type parameters are the same.

我的代码:

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

上面的代码无法编译,抱怨 firstsecond 是不同的类型。好吧,我只是很好地告诉编译器它们不是。我怎样才能让它闭嘴?

您刚刚告诉编译器,作为 TS 传递给您的 class 的类型应该相等 - 您只需要证明它们相等的证据,这可以是用于在传递实际类型时正确推断实际的 TS(但不在泛型 class 本身内)。这并不意味着 TS 可以互换。顺便说一句,它不会改变任何东西,但你通过定义新的 ST 犯了一个错误,应该是:

  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)

那么 return 类型的编译器应该推断出什么? List[T]List[S]。它唯一能做的就是List[Any]。所以同时拥有相同和不同的类型是没有意义的。如果你想要不同的名字 - 只需使用 type S = T 不需要证据。

ST 在构造函数中不同而在某些方法中相同的真实情况是什么。这在逻辑上完全不正确(在谈论抽象类型时 - 而不是具体的替换)。

顺便说一句,=:= 不是编译器功能 - 编译器没有对此进行特殊处理。整个实现在 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] implicit 接受类型 A 并给你 A =:= A - 当你需要 T =:= U 时它试图进行这样的转换,这仅适用于等类型。并且检查隐式本身的过程实际上仅在您传递实际 TU 时发生,而不是在您定义它们时发生。

关于您的具体问题:

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 建议的那样):

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

T =:= S extends T => S 并且这个隐式添加的函数(甚至作为一个对象)在 Scala 中被解释为从 TS 的隐式转换,所以它可以工作就像两种类型一样,这很酷。