Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 747
bjt38 Avatar asked May 15 '12 15:05

bjt38


2 Answers

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.)

like image 126
Rex Kerr Avatar answered Oct 09 '22 00:10

Rex Kerr


This question is old, but I think a clearer explanation is to invoke the Liskov Substitution Principle: everything that's true about a superclass should be true of all its subclasses. You should be able to do with a SubFoo everything that you can do with a Foo, and maybe more.

Suppose we have Calico <: Cat <: Animal, and Husky <: Dog <: Animal. Let's look at a Function[Cat, Dog]. What statements are true about this? There are two:

(1) You can pass in any Cat (so any subclass of Cat)

(2) You can call any Dog method on the returned value

So does Function[Calico, Dog] <: Function[Cat, Dog] make sense? No, statements that are true of the superclass are not true of the subclass, namely statement (1). You can't pass in any Cat to a Function that only takes Calico cats.

But does Function[Animal, Dog] <: Function[Cat, Dog] make sense? Yes, all statements about the superclass are true of the subclass. I can still pass in any Cat -- in fact I can do even more than that, I can pass in any Animal -- and I can call all Dog methods on the returned value.

So A <: B implies Function[B, _] <: Function[A, _]

Now, does Function[Cat, Husky] <: Function[Cat, Dog] make sense? Yes, all statements about the superclass are true of the subclass; I can still pass in a Cat, and I can still call all Dog methods on the returned value -- in fact I can do even more than that, I can call all Husky methods on the returned value.

But does Function[Cat, Animal] <: Function[Cat, Dog] make sense? No, statements that are true of the superclass are not true of the subclass, namely statement (2). I can't call all methods available on Dog on the returned value, only the ones available on Animal.

So with a Function[Animal, Husky] I can do everything I can do with a Function[Cat, Dog]: I can pass in any Cat, and I can call all of the Dog methods on the returned value. And I can do even more: I can pass in other animals, and I can call of the methods available on Husky that aren't available on Dog. So it makes sense: Function[Animal, Husky] <: Function[Cat, Dog]. The first type parameter can be replaced with a superclass, the second with a subclass.

like image 44
csjacobs24 Avatar answered Oct 09 '22 01:10

csjacobs24