Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why the first type parameter is defined as contravariant in Function1[-A, +B]?

Tags:

scala

all after reading Scala covariance / contravariance question and its answer provided by Daniel Spiewak,and Section 19.3-19.7 of book "Programming in Scala", I have another confusion about the definion of Function1[-A, +B]: why its first type parameter is contravariant? I got one reason, it is that this type parameter guarantees that the subtypes always appear earlier than supertypes when there are multiple cases, at the same time, subtype "is a" supertype. for example:

 class A
 class B extends A
 class C extends B
 scala> val withDefault: A => B = {
 | case x:B => new B
 | case x:A => new B }
 withDefault: A => B = <function1>

here, (1) case x:B is earlier than case x:A , (2) Function1[A,B] <: Function1[B,B] and there must be other reasons? any message will be appreciated!

like image 688
abelard2008 Avatar asked Apr 26 '14 08:04

abelard2008


Video Answer


1 Answers

Contravariance has nothing to do with pattern matching. Here's why function types are contravariant on their argument types. Consider this function:

def doHigherOrder(handleAnyAnimal: Animal => T,
                  anyAnimal:       Animal     ): T = {
  // ...foo...
  handleAnyAnimal(anyAnimal)
  // ...bar...
}

If functions were covariant not contravariant, then a function Duck => T would also be a subtype of Animal => T and you could do doHigherOrder(handleAnyDuck). This would be an error because then, inside doHigherOrder, the handleAnyAnimal(anyAnimal) expression would (at runtime/evaluation) boil down to handleAnyDuck(anyAnimal), which is obviously not right because a function that can handle any Duck probably can't handle any Animal:

def doHigherOrder(handleAnyDuck: Duck => T,
                  anyAnimal:     Animal   ): T = {
  // ...foo...
  handleAnyDuck(anyAnimal)  // <-- ERROR
  // ...bar...
}

Moreover, assuming Creature >: Animal, a function handleAnyCreature: Creature => T is indeed also a subtype of Animal => T, because it is OK to pass anyAnimal into something that can accept anyCreature.

That's why contravariance is intuitively inevitable with argument types.


However, return values are covariant because they have exactly the same semantics as just values:

val animal = getDuck()    // ok
val duck   = getAnimal()  // <-- ERROR

compare with

val x = handleDuck(animal)  // <-- ERROR
val y = handleAnimal(duck)  // ok

Figuratively, you assign to function arguments, but from their return values. It becomes more obvious with named arguments:

val x = handle(duck   = animal) // <-- ERROR
val y = handle(animal = duck  ) // ok
like image 172
Erik Kaplun Avatar answered Oct 05 '22 03:10

Erik Kaplun