user3508638
user3508638

Reputation: 325

Scala implicit class nested type parameter

I have implicit classes with nested type parameters to provide relevant methods to the data-object only when certain conditions are met and to store type information for later use. Somehow a nested type parameter only works with a wildcard but not with a type parameter. I do not understand why... The following piece of code works:

trait A
trait AB extends A
trait AC extends A

trait ByA[T, AX <: A]
case class ByAX[T, AX <: A]() extends ByA[T, AX]
case class Wrapper[T]()

implicit class WithByAB[T](tpe: Wrapper[_ <: ByA[T, AB]]) {
  def printsomething(): Unit = {
    println("WithByAB")
  }
}

Wrapper[ByAX[String, AB]]().printsomething()
//Wrapper[ByAX[String, AC]]().printsomething() does not compile (expected)

This one does not:

trait A
trait AB extends A
trait AC extends A

trait ByA[T, AX <: A]
case class ByAX[T, AX <: A]() extends ByA[T, AX]
case class Wrapper[T]()

implicit class WithByAB[T, S <: ByA[T, AB]](tpe: Wrapper[S]) {
  def printsomething(): Unit = {
    println("WithByAB")
  }
}

Wrapper[ByAX[String, AB]]().printsomething()
//Wrapper[ByAX[String, AC]]().printsomething() does not compile (expected)

It looks like the compiler cannot resolve type T but if the first example works and preserves the parameter-type information, why not in the second example?!

Upvotes: 1

Views: 499

Answers (1)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149518

The problem is that the scala compiler isn't able to deduce that ByAX[String, AB] actually conforms to WithByAB[T, S <: ByA[T, AB]]. It is unable to deduce that T conforms to String only from binding S.

We can see that when compiling with -Xlog-implicits:

λ scalac -Xlog-implicits Implicit.scala
Implicit.scala:22: WithByAB is not a valid implicit value for Wrapper[ByAX[String,AB]] => ?{def printsomething: ?} because:
inferred type arguments [Nothing,ByAX[String,AB]] do not conform to method WithByAB's type parameter bounds [T,S <: ByA[T,AB]]
    Wrapper[ByAX[String, AB]]().printsomething()
                             ^
Implicit.scala:22: error: value printsomething is not a member of Wrapper[ByAX[String,AB]]
    Wrapper[ByAX[String, AB]]().printsomething()
                                ^
one error found

Note how the compiler is unable to extract String from ByAX first type parameter. This is because from the compilers point of view, it has S which is bound by ByAX, but it is unaware of it's higher order or it's internal binding to String, all it can infer is a single type, merely S.

We can work around this by making Wrapper a bit more verbose

case class Wrapper[T, S]()

implicit class WithByAB[T, S <: ByA[T, AB]](tpe: Wrapper[T, S]) {
  def printsomething(): Unit = {
    println("WithByAB")
  }
}

Wrapper[String, ByAX[String, AB]]().printsomething()

Or, as you've noted, by making Wrapper bind to an existential.

Upvotes: 2

Related Questions