2

If T is covariant or contravariant, can I determine the variance of:

K >: T

or

K <: T

I ask this because I see in the book "programming in scala", it says, in the code:

trait Cat[T] {
   def meow[K]
}

The position of K is negative, it must be contravariant(Let's assume it's not a nonvariant type).

Since we can't just declare covariance or contravariance just with a single type K, we have to use upper bound or lower bound, so the K may be:

K >: T

or

K <: T

But how can I determine new type is covariant or contravariant?

Freewind
  • 193,756
  • 157
  • 432
  • 708
  • do you mean "contravariant/covariant" instead of "contravariance/covariance"? – Erik Kaplun Apr 29 '14 at 14:13
  • Thank you, sorry for my bad English – Freewind Apr 29 '14 at 14:19
  • I don't see a relation between `K` and `T` in `trait Cat[T]{ def meow[K] }` so it doesn't seem like there needs to be a relationship. – wheaties Apr 29 '14 at 14:21
  • `K` is just indicating the position in that piece of code, actually, it may be `K >: T` or `K <: T` – Freewind Apr 29 '14 at 14:30
  • Unless `K` itself is a type parameter of `Cat`, I don't see how `Cat` can have any variance relationship with `K`... unless I'm mistaken. Also, I think it's a misnomer to say that "a type is covariant/contravariant"... because a type can only be *variant with respect to one or more of its type parameters. – Erik Kaplun Apr 29 '14 at 14:43

3 Answers3

4

As I already noted in a comment, type variance can only be talked of in relation to a type parameter. A type itself isn't covariant or contravariant or invariant. Since K does not appear in the type parameter list of Cat, Cat has no variance in relation to K. Consider:

trait Cat[T] {
  def meow[K]
}

class SiameseCat[T] extends Cat[T] {
  def meow[K] = println("loud meow")
}

class Foo
class Bar extends Foo
class Baz extends Bar

val barSiamese = new SiameseCat[Bar]

// COMPILATION ERROR: personality.analysis.demo.Bar <: personality.analysis.demo.Foo, but class SiameseCat is invariant in type T
val fooSiamese: SiameseCat[Foo] = barSiamese
// SAME
val bazSiamese: SiameseCat[Baz] = barSiamese

// NO ERROR
barSiamese.meow[Foo]
barSiamese.meow[Bar]
barSiamese.meow[Baz]
barSiamese.meow[Int]
barSiamese.meow[Unit]

Arguably, in more lax speech, you can say a type is *variant if it's obviously a container type and takes just one type parameter, such as List[T]; i.e. one can say List is covariant, but this actually expands to "List[T] is covariant with respect to T".

However, if K did in fact appear in the type parameter list of Cat, it would make it possible to declare Cat as covariant with respect to K by prepending a + to K: Cat[T, +K], which will be allowed by the compiler because K only appears in variance-neutral positions in the body of Cat:

def meow[K]  // <-- meow doesn't take any parameters and returns `Unit`, so `K` is irrelevant with respect to variance

if, however, you were returning K from meow, you'd only be able to mark Cat as invariant or covariant with respect to K:

def meow: K  // contravariance made impossible

conversely, this:

def meow(x: K)  // covariance made impossible

would force you to either go with Cat[T, -K] (contravariant) or just Cat[T, K] (invariant).

For the reason why, either google, or see a recent answer of mine @ why the first type parameter is defined as contravariant in Function1[-A, +B]?

Community
  • 1
  • 1
Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
1

The type parameters of methods such as K in def meow[K] can't be co- or contravariant. The notion can only apply to type parameters of types.

Since we can't just declare covariance or contravariance just with a single type K, we have to use upper bound or lower bound

This also doesn't make sense. We can declare that MyClass is covariant in K like this:

class MyClass[+K]

This is completely unrelated to lower/upper bounds: you can have a variant type parameter with or without a bound.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
0

Two type of variance for:

class X1
class X2 extends X1
class X3 extends X2

1) Declaration-site variance:

// covariant type-constructor:
class Cat[+T]

var cat : Cat[X2] = new Cat[X2]
var cat = new Cat[X3]
// cat = new Cat[X1] // not compiled

2) Using-site variance (by upper or lower bound limitation):

//covariant variable for invariant type constructor:
class Cat[T]

var cat : Cat[_ <: X2] = new Cat[X2]
cat = new Cat[X3]
// cat = new Cat[X1] // not compiled

Decalration-site variance used in type-constructor(container) declaration and using-site variance commonly used in method or variavle declararion.

Yuriy
  • 2,772
  • 15
  • 22