score:4

Accepted answer

a is in covariant position for both contains and filter.

the signature for contains is actually:

def contains[a1 >: a](elem: a1): boolean

when a type variable is used on the right-side of a lower type bound, it is in covariant position.

with filter, the parameter is of type a => boolean, i.e. function1[a, boolean]. function1 is contravariant in its first parameter, and the function1 itself is in contravariant position as the parameter to filter. two contravariances combine to make covariance.

one way to get your head around this is image a list[x] where x <: y. if this list[x] is cast to list[y], are those methods still type-safe? contains now requires that a1 be a supertype of y, which is actually a more stringent requirement. filter now requires a function y => boolean, which is also a more stringent requirement because the passed in function must be able to handle any y, not just those that are instances of x. on the other hand, casting a list[y] to a list[x] would not be type-safe. if x also subtypes z but y does not, and z is used for a1 in contains, then the type bound will be violated, because z >: y isn't true. for filter, the passed function would be x => boolean, and couldn't safely be passed the instances of y that the list contains. so, we can conclude that a is indeed in covariant and not contravariant position in these methods.

score:1

this is allowed because for covariant types, the methods that take arguments can have a lower bound of t. consider the following example:

trait mylist[+t] {

  def prepend(elem: t): list[t] = new cons(elem, this)
}

this won't work because you can't have a covariant type as an argument - you will get a compile time error. on the other hand, you can restrict the method parameter with a lower bound of t - the argument can be t or a supertype of t, instead of a subtype, like so

trait mylist[+t] {

  def prepend[s >: t](elem: s): list[s] = new cons(elem, this)
}

Related Query

More Query from same tag