2

I've tried my best to understand how the lowerbound works related in type parameterization, but cannot understand it clearly.

Here, some example code for lowerbound.

class Queue[+T] (
  private val leading: List[T],
  private val trailing: List[T]
) {

  private def mirror = {
    if (leading.isEmpty){
      new Queue(trailing.reverse, Nil)
    }
    else
      this
  }

  def head = mirror.leading.head

  def tail = new Queue(mirror.leading.tail,
                       mirror.trailing)

  /* ====Wrong definition for enqueue====
   * def enqueue (item: T) =
   *   new Queue(leading, item :: trailing)
   */

  def enqueue[U >: T] (item: U) =
    new Queue[U](leading, item :: trailing)
}

class Fruit (val name: String) {
  override def toString = name
}
class Orange extends Fruit("orange")
class Apple  extends Fruit("apple")

val queueOrange: Queue[Orange] = new Queue[Orange](Nil, Nil)
queueOrange.enqueue(new Apple())
queueOrange.enqueue(new Orange())
queueOrange.enqueue(new Fruit("PineApple"))

val queueFruit: Queue[Fruit] = queueOrange
queueFruit.enqueue(new Orange())
queueFruit.enqueue(new Apple())
queueFruit.enqueue(new Fruit("Pineapple"))

And here is a result of the execution of the above code.

I have some questions related to above code and results.

  1. I couldn't understand how the code queueOrange.enqueue(new Apple()) can be executed successfully. As far as I know, lowerbound restrict that the type U in the enqueue method should be a supertype of T, which is a lowerbound. However, here Apple and Orange are siblings which extends the same superclass Fruit.

  2. And I couldn't understand how the queueOrange.enqueue(new Apple()) returns the Queue[Fruit] even we feed the new Orange() as enqueue's parameter. I think it could be possible because Orange inherits Fruit, but I couldn't understand how the overall mechanism works that allow question1 and question2 happen.

  3. I couldn't understand why the result different when I used the queueOrange and queueFruit to do same things in the above code.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
ruach
  • 1,369
  • 11
  • 21

1 Answers1

2

As far as I know, lowerbound restrict that the type U in the enqueue method should be a supertype of T

That is not the complete definition. U, or any of its ancestors must be a supertype of T. If we assigned that expression to a value, you'd see that the inferred type is Queue[Fruit], because the compiler is smart enough to look at the object graph.

If you wanted to make sure U is a direct supertype of T, you need to modify your method a bit:

def enqueue[U](u: U)(implicit ev: T <:< U)

This way, the compiler will first bind U to Apple, and then search for implicit evidence such that Apple :> Orange, and will fail.

The documentation for lower bounds shows this in a nutshell

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • Thanks for an answer, but I am curious why the sibling Apples can be treated as Orange's supertype? Does the compiler automatically infer the type Fruit from the Apple and apply it to the U? I am not sure how this could happen... I've read the article that you mentioned, but I think the last example only matches the last part of my example code, which is val queueFruit: Queue[Fruit] = queueOrange. Thanks! – ruach Mar 16 '18 at 09:17
  • 3
    @JaeHyuk Lee It is not `Apple` that is treated as a supertype. When the compiler attempts to bind `U`, it will fail on `Apple` and start searching the type hierarchy for a suitble match, until it reaches `Fruit`. – Yuval Itzchakov Mar 16 '18 at 09:23
  • 1
    Oh I see when the compiler find that the Apple is not a supertype of Orange, then it tries to look at the superclasses of Apple to look at whether the Apple class has any matchable supertypes of Orange. Is that right? Now everything so clear. Thanks :D – ruach Mar 17 '18 at 08:17
  • Wow thanks a lot!!! If you don't mind could you help me with this question also?? https://stackoverflow.com/questions/49334070/how-does-the-scala-compiler-locates-the-positions-for-variance-annotation – ruach Mar 17 '18 at 08:36