Scala:使用隐式证据的通用方法不能编译 [英] Scala: generic method using implicit evidence doesn't compile
问题描述
我正在通过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屋!