I tried to define a class
abstract class Sequence[+A] {
def append (x: Sequence[A]): Sequence[A]
}
and got in the terminal
<console>:8: error: covariant type A occurs in contravariant position in type Sequence[A] of value x
def append (x: Sequence[A]): Sequence[A]
Why isn't this definition OK and what would be the best way to fix this? I checked this covariant type T occurs in contravariant position but nothing helpful for me there.
As usual given his expertise in Scala, @vptheron is exactly right on the solution. But let me add a little to the "why" with an example inspired by Scala author Martin Odersky himself.
By defining Sequence[+A], you're saying if B is a subtype of A, then Sequence[B] is a subtype of Sequence[A].
I think type stuff makes a lot more sense when you take it out of the abstract (no pun intended) and use a concrete example.
Let's create a concrete implementation:
class IntSequence extends Sequence[Int] {
override def append(x: Sequence[Int]) = {
println(math.sqrt(x.head))
//makes no sense but humor me
}
}
Covariance in A means that because Int is a subtype of Any, Sequence[Int] is a subtype of Sequence[Any]. So far so good.
Now imagine a concrete class StringSequence defined in a similar way to IntSequence.
Then let's do this:
val x:Sequence[Any] = new IntSequence
val ss:Sequence[Any] = new StringSequence
//Let ss be populated somehow
x.append(ss)
The first line is valid of course. IntSequence is a subclass of Sequence[Int], and Sequence[Int] is a subtype of Sequence[Any] by covariance.
The second line is valid of course. For a similar reason.
The third line (the line after the comment) is valid of course. Sequence[String] is a subclass of Sequence[Any], so I can append a Sequence[String] to a Sequence[Any].
But when I try the two lines together, I have now said that I can take the square root of the first element of my Sequence[String] because x is really a IntSequence.
To borrow a line from the American movie Apollo 13, "Houston, we have a problem."
This is why you get that error. To generalize, as soon as a generic parameter appears as the type of a parameter to a method, the class that contains that method can't be covariant in that type because you get exactly that kind of weirdness.
To get around this, you need a lower bound for the type parameter in the method definition. In other words, the type in the method parameter must be a supertype of the covariant type in the class definition.
Or more simply, do what @vptheron does.
Hope that helps.
This works:
abstract class Sequence[+A]{
def append[B >: A](x: Sequence[B]): Sequence[B]
}
When you define a covariant type you can't use it as an input parameter (you would have the same problem with a contravariant type used for a returned type). The workaround is to define a new type (here B) that is a super type of A.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With