Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Scala compiler say that contravariant type A occurs in covariant position in type >: A <: Any of type B?

The compiler is telling me this can't be with a warning of: "contravariant type A occurs in covariant position in type >: A <: Any of type B." The warning is in the type parameter of the compose method. Logically the type definition makes sense to me. If the compiler has no qualms with andThen, why the problem with the converse?

trait Foo[-A]{
  def compose[B >: A](t: Foo[B]): Foo[A] = t andThen this
  def andThen[B <: A](t: Foo[B]): Foo[B]
}

All I need is an example where it breaks down. Then I'm happy.

like image 839
wheaties Avatar asked Feb 03 '13 23:02

wheaties


People also ask

What is covariant in Scala?

Covariance is a concept that is very straightforward to understand. We say that a type constructor F[_] is covariant if B is a subtype of type A and F[B] is a subtype of type F[A]. In Scala, we declare a covariant type constructor using the notation F[+T], adding a plus sign on the left of the type variable.

What is covariance and Contravariance in Scala?

Covariance allows assigning an instance to a variable whose type is one of the instance's generic type; i.e. supertype. Contravariance allows assigning an instance to a variable whose type is one of the instance's derived type; i.e. subtype.

Are lists covariant in Scala?

The Scala standard library has a generic immutable sealed abstract class List[+A] class, where the type parameter A is covariant. This means that a List[Cat] is a List[Animal] .


1 Answers

As the error says, your variance annotation of A is wrong. You cannot use A in a return type which is a covariant position. Imagine you had another method in Foo which uses A in proper contravariant position (as an argument):

trait Foo[-A] {
  ...
  def foo(a: A): Unit
}

Now you can see how this crashes:

  • Foo[-A] implies that Foo[X] <: Foo[Y] if X >: Y
  • a returned value may be a subtype of the declared return type
  • therefore, if -A was legal here, compose might return a Foo[A1] for some A1 >: A
  • say trait X and trait Y extends X { def bar() }
  • imagine a Foo[Y] where foo calls a.bar()
  • consequently it would break if compose was allowed to return Foo[X]

So for you example to compile, A must be invariant.

like image 127
0__ Avatar answered Sep 28 '22 16:09

0__