简体   繁体   中英

Scala covariance, contravariance confusion

I am a newcomer to Scala and it's really very confusion. Please help me.

/**
2    * Remember! In Scala, every function that takes one argument 
3    * is an instance of Function1 with signature:
4    *
5    * trait Function1[-T, +S] extends AnyRef
6    */
7   
8   class Vehicle(val owner: String)
9   class Car(owner: String) extends Vehicle(owner)
10  
11  object Printer {
12
13    val cars = List(new Car("john"), new Car("paul"))
14
15    def printCarInfo(getCarInfo: Car => AnyRef) {
16      for (car <- cars) println(getCarInfo(car))
17    }
18  }
19  
20  object Customer extends App {
21
22   val getOwnerInfo: (Vehicle => String) = _.owner
23   
24   Printer.printCarInfo(getOwnerInfo)
25  }

This code is taken from https://medium.com/@sinisalouc/variance-in-java-and-scala-63af925d21dc

The Rule is:

This rule of function being covariant in its input type and contravariant in its return type comes from the Liskov substitution principle (LSP). It says that T is a subtype of U if it supports the same operations as U and all of its operations require less (or same) and provide more (or same) than the corresponding operations in U (subtyping is reflexive so S <: S).

So my question is if I can pass a subtype where a super type is required (above code line number 15 and 22) then why the following code does not works?

class MyClass extends AnyRef
class MySubClass extends MyClass

abstract class Class {
  val f1: (Any) => Any = ???
  val f2: (Any) => Boolean = ???
  val f3: (MyClass) => Any = ???
  val f4: (MySubClass) => Boolean = ???
  val f5: (Any) => Nothing = ???
  val f6: (MyClass) => Null = ???

  val f: (MyClass) => Boolean = f4; //Error
}

Update So actually it's like passing a param to the function so I have param can be contravariant [-T] and return can be covariant [+S]

    class MyClass extends AnyRef
    class MySubClass extends MyClass

    abstract class Class {
      val f1: (Any) => Any = ???
      val f2: (MyClass) => Boolean = ???
      val f3: (MyClass) => Any = ???
      val f4: (MySubClass) => Boolean = ???
      val f5: (Any) => Nothing = ???
      val f6: (MyClass) => Null = ???

      val f: (MySubClass) => AnyVal = f2
}

This is a valid code as MyClass is like going Up in the hierarchy and Boolean is like cuming down in the hierarchy.

Your code won't compile because otherwise eg if you had a

class MyOtherSubClass extends MyClass

your

val f: (MyClass) => Boolean

could accept a MyOtherSubClass as a parameter eg f(new MyOtherSubClass()) , but that would be calling f4(new MyOtherSubClass()) . But MyOtherSubClass isn't a MySubClass , so you'd be calling f4 with the wrong type

Lets looks into printCarInfo parameter. It's little function that accept Car as an argument and returns AnyRef : getCarInfo: Car => AnyRef .

In scala such functions with one argument can be represented using trait Function1[-T, +S] extends AnyRef

Function1 parametrised by -T and +S two types. First type represent argument of function. Car in our case. And it's contra-variant (minus sign), which means you can pass any super-type . +S represents return type and it's covariant (plus sign), which means you can pass any sub-type.

printCarInfo invoked and parameter of type Vehicle => String passed. Vehicle is super type for Car and String is subtype for AnyRef, so it satisfy criteria.

So why arguments are in contra variant position and return type in covariant. Let's try to assume opposite for arguments:

printCarInfo accept function of type: Vehicle=>AnyRef and getOwnerInfo is Car=>AnyRef

When you trying to implement printCarInfo we can process any argument, not just cars, but lorries and bicycles. Obviously invocation of Printer.printCarInfo(getOwnerInfo) fails as you're trying to getOwnerInfo from lorry or bicycle, while your method implementation can deal with cars only. Hopefully it's clear.

So regarding your code. It's opposite: you can assign f4: MysSubClass => Boolean = f and it will work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM