Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala implicit conversion is applying under some conditions but not others

Here is a simple reproducer, where I define a "commutative" pair type that comes with an implicit reordering conversion. The implicit conversion is applied by the compiler as expected if the argument to function f is in a pre-existing named value (t in the example). However, if I try to call f directly on literal CommutativePair, it fails with a type error. The compiler is not applying the implicit reordering conversion in that case.

object repro {
  import scala.language.implicitConversions

  case class CommutativePair[A, B](a: A, b: B)

  object CommutativePair {
    // Support a kind of commutative behavior via an implicit reordering
    implicit def reorderPair[B, A](pair: CommutativePair[B, A]) =
      CommutativePair(pair.b, pair.a)
  }

  // The idea is to allow a call to 'f' with Pair[Int, String] as well,
  // via implicit reorder.
  def f(p: CommutativePair[String, Int]) = p.toString

  val t = CommutativePair(3, "c")

  // This works: the implicit reordering is applied
  val r1 = f(t)

  // This fails to compile: the implicit reordering is ignored by the compiler
  val r2 = f(CommutativePair(3, "c"))
}
like image 815
eje Avatar asked Aug 25 '15 18:08

eje


1 Answers

I believe it is hitting the limit of scala inference, triggered by the order in which it searches for solutions. In the first situation:

val t = CommutativePair(3, "c")

The inference has locked the type to CommutativePair[Int,String], because it is the only one that could work based on the parameters. So when it calls:

val r1 = f(t)

It gets a type mismatch on Commutative[Int,String] =!= Commutative[String,Int], then it searches for implicits and finds the one above.

In the second case, scala is trying to figure out the types, working its way in from the outside:

val r2 = f(CommutativePair(3, "c"))
  • First, it determines that f, must take Commutative[String,Int].
  • Then, it follows that CommutativePair(...,...) must be Commutative[String,Int] (since, it hasn't figured out it's type from the it's parameters, yet).
  • Now it looks at the parameters to CommutativePair(...) and finds them to be the wrong type. But this won't trigger the implicit conversion because it thinks the mismatch is in the parameters, not on the whole CommutativePair(...).

Indeed, locking down type params (by passing them explicitly or by binding to a val first) fixes the error.

like image 179
Alvaro Carrasco Avatar answered Sep 28 '22 14:09

Alvaro Carrasco