Scala: generic method using implicit evidence doesn't compile

后端 未结 1 1240
忘了有多久
忘了有多久 2021-01-15 14:17

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

相关标签:
1条回答
  • 2021-01-15 15:00

    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.

    0 讨论(0)
提交回复
热议问题