Scala:使用隐式证据的通用方法不能编译 [英] Scala: generic method using implicit evidence doesn't compile

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

问题描述

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


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


我的代码:

  class Pair [T,S](var first:T,var second:S){
def swap [T,S](imp ev:T =:= S){
val temp = first
first = second //不会编译
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){//没有[S,T] - 它们与原始的无关,它是全新的名字
val temp = first
first = second //不编译
second = temp
}
}

然而它仍然没有不会编译。为什么?试想一下:
$ b $ pre $ def $ getBoth(imp ev:T =:= S)= List(first,second)

那么编译器应该推断哪种返回类型? 列表[T] 列表[S] 。它唯一可以做的是 List [Any] 。所以相同和不同类型的模拟没有任何意义。如果你想要不同的名字 - 只需使用 type S = T 就不需要证据了。



什么是在 S T 的构造函数中有一些不同的方法,这在逻辑上是不正确的(当谈论抽象类型 - 不是具体的替代品)。

btw, =:= 是而不是编译器功能 - 编译器中没有特殊处理。整个实现在scala中 Predef

  @implicitNotFound(msg =不能证明$ {从} =:= $ {到}。)
封闭的抽象类=:[From,To] extends(From =&To;)Serializable
private [这]最终val单身_ =:= =新= = = [任何,任何] {def应用(x:任何):任何= x}
对象=:{
隐式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(imp ev:T =:S){
val temp = first
first = second.asInstanceOf [T]
second = temp.asInstanceOf [S]
}
}

scala>新对(5,6)
res9:Pair [Int,Int] = Pair @ 6bfc12c4

scala> res9.swap

scala> res9.first
res11:Int = 6

或者只是(如@mz和@Imm建议):

  class Pair [T,S](var first:T,var second:S){
def swap(imp ev:T =:= S,ev2:S =:= T){
val temp = first
first = second
second = temp
}

$ / code>

T =:= S 延伸 T => S 并且这个隐式添加的函数(甚至作为一个对象)被解释为从 T S 在Scala中,所以它适用于两种类型都是平等的,这非常酷。


I'm learning Scala by working the exercises from the book "Scala for the Impatient". One question asks:

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.

My code:

class Pair[T, S](var first: T, var second: S) {
  def swap[T, S](implicit ev: T =:= S) {
    val temp = first
    first = second // doesn't compile
    second = temp
  }
}

The code above fails to compile with the complaint that first and second are different types. Well, I just nicely told the compiler that they're not. How can I tell it to shut up?

解决方案

You've just told the compiler that types passed to your class as T and S should be equal - you just require an evidence of their equality, which can be used to infer actual T and S correctly when you pass actual types (but not inside generic class itself). It doesn't mean that T and S are interchangable. Btw, it doesn't change anything but you did a mistake by defining new S and T, should be:

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

However it's still doesn't compile. Why? Just think of it:

 def getBoth(implicit ev: T =:= S) = List(first, second)

So what is return type compiler should infer? List[T] or List[S]. the only it can do is List[Any]. So having same and different types simulteniously has no sense. If you want different names - just use type S = T no evidence will be needed.

And what is the real case of having S and T different in constructor and same in some method. It's simply logically incorrect (when talking about abstract types - not concrete substitutions).

Btw, =:= is not the compiler feature - there is no special processing for that in compiler. The whole implementation is it inside 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]
  }

So it's just tpEquals[A] implicit which takes type A and gives you A =:= A - when you require T =:= U it's trying to make such conversion which is possible only for equal types. And the process of checking implicit itself actually happens only when you pass actual T and U, not when you defining them.

About your particular problem:

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

Or just (as @m-z and @Imm suggested):

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 and this implicitly added function (even as an object) is interpreted as implicit conversion from T to S in Scala, so it works like both types are equal, which is pretty cool.

这篇关于Scala:使用隐式证据的通用方法不能编译的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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