I\'m learning Scala by working the exercises from the book \"Scala for the Impatient\". One question asks:
Given a mutable
Pair[S, T]
cla
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.