1

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?!

user3508638
  • 195
  • 1
  • 7

1 Answers1

2

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.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • Thanks for your answer Yuval. I already went down the road of adding an extra parameter to the wrapper but I actually need the wrapper to also contain types without the sub-type T parameter. Hence I cannot store it explicitly inside the wrapper. So I guess I have to stick to my first example to match on subtypes of ByA and extract T without the ability to reuse the complete type. – user3508638 Jan 31 '18 at 11:57
  • @user3508638 If that works for you when using an existential, then go for it. – Yuval Itzchakov Jan 31 '18 at 11:58