3
sealed trait List[+A] // `List` data type, parameterized on a type, `A`
case object Nil extends List[Nothing] // A `List` data constructor representing the empty list
/* Another data constructor, representing nonempty lists. Note that `tail` is another `List[A]`,
which may be `Nil` or another `Cons`.
 */
case class Cons[+A](head: A, tail: List[A]) extends List[A]

My question is what is the "+" in front of A? Why in here "List[A]" the plus is ignored?

Thanks

BufBills
  • 8,005
  • 12
  • 48
  • 90
  • 2
    http://stackoverflow.com/questions/663254/why-doesnt-the-example-compile-aka-how-does-co-contra-and-in-variance-w – vvg Nov 06 '15 at 23:56
  • 2
    I read that well-written post, linked by `Mr. V.`, a few times before understanding it. So don't be discouraged. – Kevin Meredith Nov 07 '15 at 01:48

1 Answers1

8

A plus or minus sign in front of a type constructor argument means that values of that type appear in covariant (+) or contravariant (-) position. A covariant position means the type only ever occurs as "output" or return type, as is the case with List. Then a List[A] is a subtype of a List[B] if A <: B, if A is a sub-type of B:

trait Animal { def food: String }
case class Dog(name: String) extends Animal { def food = "all" }

def test(xs: List[Animal]) = xs.map(_.food)

test(List(Dog("Jussi")))

Here you can pass a List[Dog] for a List[Animal] because List[Dog] <: List[Animal].

Contravariance is the opposite - the type only occurs as input. For example Function1[A, Out] <: Function1[B, Out] if A >: B, if A is a super-type of B.

def test(fun: Dog => String): String = fun(Dog("Jussi"))

test { x: Animal => x.food }

Here you can pass a Animal => String for a Dog => String because the former is sub-type of the latter.


The variance annotation + or - only ever occurs in the definition of the type, so in the definition of List[+A], not anywhere else where List is used, e.g. as type ascription or in the extends clause, because the variance cannot change once it's defined. This is called definition-site variance.


Because Nothing is the bottom type in Scala, a type that is the sub-type of any other type, we can thus have the convenient object Nil extends List[Nothing], and thereby Nil becoming the sub-type of List[A] for any possible A. Whenever you need a list, no matter what the element type, you can use Nil.

0__
  • 66,707
  • 21
  • 171
  • 266