Search code examples
scalacovariancecontravariance

Why does Scala function allow subclass parameter when specifyied super class parameter


<: seems to work like I'd expect, however, >: does not.

object TheCakeIsALie extends App {
  class Food
  class Junk extends Food
  class Cake extends Junk

  val food = new Food
  val junk = new Junk
  val cake = new Cake

  def subJunk[T <: Junk](food: T) = println(s"${food.getClass.getSimpleName} <: Junk")
  // subJunk(food)
  subJunk(junk)
  subJunk(cake)

  def superJunk[T >: Junk](food: T) = println(s"${food.getClass.getSimpleName} >: Junk")
  superJunk(food)
  superJunk(junk)
  superJunk(cake) // The cake is a lie!
}

subJunk(food) is commented out because, as expected, it produces a compile time error. I expected superJunk(cake) to do the same.


Solution

  • If you want to forbid both subJunk(food) and superJunk(cake) you should better use implicit type constraints rather than type bounds.

    def subJunk[T](food: T)(implicit ev: T <:< Junk) = println(s"${food.getClass.getSimpleName} <: Junk")
    // subJunk(food) // doesn't compile
    subJunk(junk)
    subJunk(cake)
    
    def superJunk[T](food: T)(implicit ev: Junk <:< T) = println(s"${food.getClass.getSimpleName} >: Junk")
    superJunk(food)
    superJunk(junk)
    // superJunk(cake) // doesn't compile
    

    https://blog.bruchez.name/2015/11/generalized-type-constraints-in-scala.html