0
% scala                                                                     
Welcome to Scala 2.12.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_111).
Type in expressions for evaluation. Or try :help.

scala>   trait Op[-Y, -Z, +A, +B] {
     |     def apply(other: (Y, Z)): (A, B)
     |   }
defined trait Op

scala>   implicit class RichTuple2[+A, +B](t: (A, B)) {
     |     def ~~~(other: Int): (A, B) = ???
     |     def ~~~[RA, RB](other: Op[A, B, RA, RB]): (RA, RB) = other.apply(t)
     |   }
defined class RichTuple2

scala>   def swap[A, B] = new Op[A, B, B, A] {
     |     override def apply(other: (A, B)) = (other._2, other._1)
     |   }
swap: [A, B]=> Op[A,B,B,A]

scala> (1, "foo") ~~~ swap
<console>:14: error: overloaded method value ~~~ with alternatives:
  [RA, RB](other: Op[Int,String,RA,RB])(RA, RB) <and>
  (other: Int)(Int, String)
 cannot be applied to (Op[Nothing,Nothing,Nothing,Nothing])
       (1, "foo") ~~~ swap

If I remove the first ~~~(other: Int) method, then it works:

scala>   trait Op[-Y, -Z, +A, +B] {
     |     def apply(other: (Y, Z)): (A, B)
     |   }
defined trait Op

scala>   implicit class RichTuple2[+A, +B](t: (A, B)) {
     |     def ~~~[RA, RB](other: Op[A, B, RA, RB]): (RA, RB) = other.apply(t)
     |   }
defined class RichTuple2

scala>   def swap[A, B] = new Op[A, B, B, A] {
     |     override def apply(other: (A, B)) = (other._2, other._1)
     |   }
swap: [A, B]=> Op[A,B,B,A]

scala> (1, "foo") ~~~ swap
res0: (String, Int) = (foo,1)

The question is why type inference and method selection fails in this case? The method ~~~(other: Int) takes a parameter that isn't at all related to the type of swap (with is an Op type). And is anyone aware of a workaround?

Chris Leishman
  • 1,777
  • 13
  • 19

1 Answers1

1

scalac sometimes has trouble finding the right implicits or inferring the right types when one mixes implicits with overloading.

There are several jira tickets on this topic, and this particular one: SI-9523, appears to be the same problem as the one in your question.

In your case scalac is unable to infer the type arguments for swap when ~~~ is overloaded, so annotating it with swap[Int, String] should work.

Overloading is generally discouraged in Scala (see Why "avoid method overloading"?) and (http://www.wartremover.org/doc/warts.html), so the best solution is to avoid overloading.

Ziyang Liu
  • 810
  • 4
  • 10
  • Ok. So it's a(nother) Scala bug :) – Chris Leishman Oct 09 '17 at 21:11
  • Actually this is a limitation of overloading in general and not a bug related to implicits. You are asking a bit too much of the compiler by having it select the appropriate overload *and* inferring the type arguments to your `swap` method in one go. It is [specced](https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#overloading-resolution) in the paragraphs about overloading resolution and local type inference. – Jasper-M Oct 09 '17 at 22:50