Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why/How does this scala contravariant example work?

Tags:

scala

This code seems to set up a contravariant example: FruitBox can take in Apples or Oranges.

class Fruit(name: String) { }
case class Apple(name: String) extends Fruit(name) {}
case class Orange(name: String) extends Fruit(name) {}

class Box[-T] {
  type U >: T
  def put(t: U): Unit= {box += t}
  val box = scala.collection.mutable.ListBuffer[U]()
}

  object test {
   val FruitBox = new Box[Fruit]
   // Fruit Box takes in everything
   FruitBox.put(new Fruit("f1"))
   FruitBox.put(new Orange("o1"))
   FruitBox.put(new Apple("a1")) 

   // and Orange Box takes in only oranges
    val OrangeBox = new Box[Orange]
     //OrangeBox.put(new Apple("o2")  ==> Compile Error that makes sense
    OrangeBox.put(new Orange("o2"))

   // Contra-variant nature is also demonstrated by
     val o: Box[Orange] = FruitBox 
  }

That is all fine...but why does it work ? specifically: 1. When the FruitBox is initialized, why does the "type U >: T" not constrain it to supertypes of Fruit? In spite of that constraint, the FruitBox is able to put the subtypes if Fruit ( oranges and apples )...how?

like image 328
user7938511 Avatar asked Dec 15 '25 04:12

user7938511


1 Answers

First, while Scala allows you to write new Box[Fruit], leaving U an abstract member, I don't understand why. However, Scala seems to assume U = T in this case. Since your code never implements U, it can just be replaced by T. So you end up with def put(t: Fruit) in FruitBox: of course it accepts Apples, since they are Fruits! The only thing Scala knows about U is that it's a supertype of T; thus T is a subtype of U, and so is every subtype of T. So any subtype of Fruit can be passed to FruitBox.put. So def put(t: U): Unit is effectively the same as put(t: T): Unit unless you implement U as in new Box[Fruit] { type U = Object }.

a FruitBox that can put apples and oranges, and an OrangeBox that can only put oranges. That looks like contra-variant behavior, and I am fine with it.

It isn't at all contravariant behavior; you'll get exactly the same with

class Box[T] {
  def put(t: T): Unit= {box += t}
  val box = scala.collection.mutable.ListBuffer[T]()
}
like image 145
Alexey Romanov Avatar answered Dec 16 '25 21:12

Alexey Romanov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!