Contravariance, type alias and extensions

I was trying to create an extension method for cats.kleisli and I came across to this behavior that I cannot really explain.

Creating an implicit class with typeAlias argument for a class that has a contravariant type makes the methods impossible to call.
Removing the contravariant annotation from class A seems to work.
But why?

The following code doesn’t compile because of “result2” and “result4”

  final case class A[-I, O](f: I => O)

  implicit class AOps[I, O](a: A[I, O]) {
    def test1: Boolean = false

  type ATypeAlias[I, O] = A[I, O]
  implicit class ATypeAliasOps[I, O](a: ATypeAlias[I, O]) {
    def test2: Boolean = false

  val a1: A[Int, Unit]          = A((i: Int) => ())
  val a2: ATypeAlias[Int, Unit] = A((i: Int) => ())

  val result1: Boolean = a1.test1
  val result2: Boolean = a1.test2  // value test2 is not a member of example.ATypeAlias[Int,Unit]
  val result3: Boolean = a2.test1
  val result4: Boolean = a2.test2  //  value test2 is not a member of example.ATypeAlias[Int,Unit]

1 Like

It also works if you define the type alias with the same variance.

type ATypeAlias[-I, O] = A[I, O]

-Xlog-implicits says:

       ATypeAliasOps is not a valid implicit value for a2.type => ?{def test2: ?} because:
       type mismatch;
        found   : a2.type (with underlying type ATypeAlias[Int,Unit])
        required: ATypeAlias[I,Unit]
           (which expands to)  A[I,Unit]

Which is still not very helpful. But contravariance is known not to work very well with implicits and typeclasses in Scala 2. Maybe it’s related, cause it does work in Scala 3. Or maybe Scala 3 just does some (de)aliasing things differently.

1 Like