Search code examples
functionscalacovariancecontravariance

Why is Function[-A1,...,+B] not about allowing any supertypes as parameters?


I believe one can define covariance (at least, for objects) as 'the ability to use a value of a narrower (sub) type in place of a value of some wider (super) type', and that contravariance is the exact opposite of this.

Apparently, Scala functions are instances of Function[-A1,...,+B] for contravariant parameter types A1, etc. and covariant return type, B. While this is handy for subtyping on Functions, shouldn't the above definition mean I can pass any supertypes as parameters?

Please advise where I'm mistaken.


Solution

  • Covariance and contravariance are qualities of the class not qualities of the parameters. (They are qualities that depend on the parameters, but they make statements about the class.)

    So, Function1[-A,+B] means that a function that takes superclasses of A can be viewed as a subclass of the original function.

    Let's see this in practice:

    class A
    class B extends A
    val printB: B => Unit = { b => println("Blah blah") }
    val printA: A => Unit = { a => println("Blah blah blah") }
    

    Now suppose you require a function that knows how to print a B:

    def needsB(f: B => Unit, b: B) = f(b)
    

    You could pass in printB. But you could also pass in printA, since it also knows how to print Bs (and more!), just as if A => Unit was a subclass of B => Unit. This is exactly what contravariance means. It doesn't mean you can pass Option[Double] into printB and get anything but a compile-time error!

    (Covariance is the other case: M[B] <: M[A] if B <: A.)