54

I was looking at the documentation for PartialFunction in this link:

trait PartialFunction[-A, +B] extends (A) ⇒ B

Maybe someone can help clarify the significance of the plus and minus signs in the generic declaration?

Ariel T
  • 2,879
  • 1
  • 21
  • 21

3 Answers3

33

"+" and "-" mean covariant and contravariant types respectively. In short, it means that:

PartialFunction[-A1, +B1] <: PartialFunction[-A2, +B2] only if A1 :> A2 and B1 <: B2, where <: is a subtyping relationship.

"-" usually applied for input parameters, "+" for output - in C# they even use respective keywords in and out. There is also some more primitive generic variance support in Java built up on existential types - actually you can do it using _ <: SomeType (covariance) or abstract type members type T <: SomeType in Scala as well.

Without modifiers PartialFunction[A1, B1] would have no direct relationship to a PartialFunction[A2, B2] (in other words, it would be invariant).

P.S. There are also some restrictions applied to such types, like covariant("+") type can't be in contravariant position (you can only return it from a method) and vice-versa. This is done in order to support Liskov Substitution Principle and naturally understandable by "in"/"out" interpretation.

Also, it worth noting that A => B (syntax sugar for Function1) itself is using co-/contra-variance:

 trait Function1 [-T1, +R] extends AnyRef

As those functions can be extended through sub-typing which makes them theoretically partial as well (though it’s not how Scala treats these) - even technically “total” FunctionN in Scala could be extended, redefined, return null and so on.

dk14
  • 22,206
  • 4
  • 51
  • 88
23

It's covariance and contravariance. https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)

Basically it says for Generic types how inheritance will work. Easy sample from Scala is - trait Seq[+A] Because of the + , the code

val s: Seq[Person] = Seq[Student]()

will compile because Student extends Person. Without the + it won't work

A bit more complex sample -

class C[-A, +B] {
  def foo(param: A): B = ???
}

class Person(val name: String)

class Student(name: String, val university: String) extends Person(name)

val sample: C[Student, Person] = new C[Person, Student]
Maxim
  • 7,268
  • 1
  • 32
  • 44
8

To supplement the other answers, here is a link to the documentation for variances on the scala-lang site:

https://docs.scala-lang.org/tour/variances.html

Andre
  • 1,577
  • 1
  • 13
  • 25
Michael Hewson
  • 1,444
  • 13
  • 21