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
}
}
上面的代码无法编译,抱怨 first
和 second
是不同的类型。好吧,我只是很好地告诉编译器它们不是。我怎样才能让它闭嘴?
您刚刚告诉编译器,作为 T
和 S
传递给您的 class 的类型应该相等 - 您只需要证明它们相等的证据,这可以是用于在传递实际类型时正确推断实际的 T
和 S
(但不在泛型 class 本身内)。这并不意味着 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)
那么 return 类型的编译器应该推断出什么? List[T]
或 List[S]
。它唯一能做的就是List[Any]
。所以同时拥有相同和不同的类型是没有意义的。如果你想要不同的名字 - 只需使用 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]
implicit 接受类型 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 建议的那样):
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 中被解释为从 T
到 S
的隐式转换,所以它可以工作就像两种类型一样,这很酷。
我正在通过书中的练习来学习 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
}
}
上面的代码无法编译,抱怨 first
和 second
是不同的类型。好吧,我只是很好地告诉编译器它们不是。我怎样才能让它闭嘴?
您刚刚告诉编译器,作为 T
和 S
传递给您的 class 的类型应该相等 - 您只需要证明它们相等的证据,这可以是用于在传递实际类型时正确推断实际的 T
和 S
(但不在泛型 class 本身内)。这并不意味着 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)
那么 return 类型的编译器应该推断出什么? List[T]
或 List[S]
。它唯一能做的就是List[Any]
。所以同时拥有相同和不同的类型是没有意义的。如果你想要不同的名字 - 只需使用 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]
implicit 接受类型 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 建议的那样):
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 中被解释为从 T
到 S
的隐式转换,所以它可以工作就像两种类型一样,这很酷。