Contravariant method argument type
Asked Answered
V

2

4

wiki Contravariant_method_argument_type says overriding method has the subtyping rule as function type, but no language except one support contravariant argument type. I also not able to come up with any idea of benefit to use that.

example:

class AnimalShelter {
    Animal getAnimalForAdoption() {      ...        }         
    void putAnimal(Animal animal) {      ...        }   
}

class CatShelter extends AnimalShelter {
    @Overriding        
    Cat getAnimalForAdoption() {  return new Cat();    }        
    @Overriding                    
    void putAnimal(Object animal) {      …        }     
}

My question is:

  1. Is contravariant argument type of overriding method any of good use? if yes, where it is?
  2. Is method a function? Why Scala has different rule for function type and overriding method type?
Vengeance answered 23/3, 2014 at 4:9 Comment(0)
H
10

Is contravariant argument type of overriding method any of good use? if yes, where it is?

Example translated from the Sather documentation:

interface Carnivore {
  void eat(Meat food);
}

interface Herbivore {
  void eat(Plant food);
}

interface Omnivore extends Carnivore, Herbivore {
  // overrides both above eat methods,
  // since Meat and Plant are subtypes of Food
  void eat(Food food);
}

Is method a function?

In Scala? No, but it can be converted to a function.

Why Scala has different rule for function type and overriding method type?

Because overriding method types has to follow JVM's rules. It could be done by creating bridge methods (in the case above, adding methods eat(Plant) and eat(Meat) which just call eat(Food)), similarly to the way covariant return type is implemented, but it would add complexity to the language without much benefit.

Horseshoes answered 23/3, 2014 at 5:43 Comment(0)
N
3

I can also add one example from Spray toolkit, particularly Marshaller trait. In general you can think about Marshallers as a function with converts some entity of type T into HttpEntity (for http response), but with some internal tricks, so actually it is implemented as (T, Context) => Unit, where HttpEntity is generated by this Contenxt. Anyway if you take a look at its declaration you'll see that it's type T is in contravariant position:

trait Marshaller[-T] {
  def apply(value: T, ctx: MarshallingContext)
}

Semantically you can think think about this in terms of a simple function that returns a Unit. And here contravariance is natural. Lets say that you have a simple hierarchy:

sealed trait ServerInfo {
  def data: DataTime
}

case class ServiceStatus(status: String, data: DateTime = DateTime.now) extends ServerInfo

With this marshallers:

val serverInfoMarshaller: Marshaller[ServerInfo] = ???
val serverStatusMarshaller: Marshaller[ServerStatus] = ???

And you have a function which return this status:

def response(data: ServiceStatus, marshaller: Marshaller[ServiceStatus]): Unit = ???

But because marshaller is contravariant, you can also use not only serverStatusMarshaller: Marshaller[ServerStatus], but also serverInfoMarshaller: Marshaller[ServerInfo], cause it also knows how to serialize ServerStatus into the correct response for the user.

Nuncupative answered 23/3, 2014 at 8:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.