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

Abh*_*kar 2 generics types scala implicit-conversion

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

给定一个可变Pair[S, T]类,使用类型约束来定义一个交换方法,如果类型参数相同,则可以调用该方法.

我的代码:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码失败,并抱怨编译firstsecond是不同的类型.好吧,我很好地告诉编译器他们不是.怎么能告诉它闭嘴?

dk1*_*k14 8

你刚刚告诉类型传递给你的类,因为编译器TS应该是平等的-你只需要他们的平等,它可以被用来推断实际的证据T,并S正确地当你通过实际的类型(但通用类本身不中) .这并不意味着,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
    }
  }
Run Code Online (Sandbox Code Playgroud)

但它仍然无法编译.为什么?试想一下:

 def getBoth(implicit ev: T =:= S) = List(first, second)
Run Code Online (Sandbox Code Playgroud)

那么返回类型编译器应该推断出什么?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]
  }
Run Code Online (Sandbox Code Playgroud)

所以它只是tpEquals[A]隐含的,它采用类型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
Run Code Online (Sandbox Code Playgroud)

或者只是(正如@mz和@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
    }
}
Run Code Online (Sandbox Code Playgroud)

T =:= S延伸T => S,这隐含地添加功能(甚至作为一个对象)被解释为从隐式转换TS在Scala中,所以它的工作原理是这两种类型是相等的,这是相当凉爽.

  • @ dk14你可以通过要求`T =:= S`和`S =:= T`来解决这个问题. (2认同)