views:

131

answers:

2

Hi,

In Scala variance can be defined with variance operators like + and - on the generic type argument. For example the List type is covariant in the standard library.

class List[+A]

So a function with a covariant list can be defined like this:

def foo[A](list : List[A])

Also variance can be emulated with generic bounds. So we can also write this

def foo[A](list : List[_:< A])

of course this makes no sense, because list is already covariant. But the same trick could be done for types that are not covariant. (like Stack). Of course, also a new types could be created from stack (inheritance of aggregation) that is covariant.

So my questions:

  1. When should be use generic bounds for variance? And when should we create a new covariant type?
  2. Are generic bounds only useful for variance, or can they declare more (language concepts).
  3. If they are only useful for variance, are bounds then only for compatibility with Java?

thx in advance :)

+3  A: 
  1. When creating a new generic type, say Foo[T], you should try hard to determine whether that type is covariant, contravariant or invariant and declare it Foo[+T], Foo[-T] or Foo[T] respectively. Admittedly this can be a bit difficult. However, it frees the user of Foo of making that decision every time she needs to use a Foo by using generic bounds. In short: prefer declaration site variance over call site variance when the variance is a property of the type itself.

BTW, the Programming in Scala book by Martin Odersky, Lex Spoon and Bill Venners has some great seactions about variance. See Chapter 19 Type Parametrization.

michid
+8  A: 

If a type is naturally covariant or contravariant you should declare it so. Your users will thank you for it. The use-site variance is indeed mostly there because of Java. More precisely, a type such as Array[T <: Number] is treated as a shorthand for an existential type:

ArrayBuffer[T] forSome { type T <: Number }

Existential types have a pretty bulky syntax in Scala. That's sort of intentional, because we do not recommend you use them much. When would you need an existential type?

  1. To write the analogue of a Java type with wildcards, such as List<? extends Number>.
  2. To write the analogue of a Java raw type, such as List.

In Java, raw types and wildcard types are not quite the same and neither is quite the same as an existential type (even though we know what they are not, it's pretty hard to state precisely what they are). But they are close enough to existentials in practice, so that Scala gets away with mapping them to this kind of type.

Martin Odersky