Search code examples
scalaupperbound

Scala upper bound notation accepts all types (not only super type)


In Scala's upper bound concept, the given type or its super type can be passed. For example, in the below method S is the type and A is the parameter we pass. This method accepts all the values present in Scala's type system actually. S, S subtype and its super type. This is due to the fact that all types extends Any type.

def method[A >: S](a:A) = { ... }

Then why can't we write all the upper bound notations as Any (which is the universal type in Scala). The above definition can be re-written as:

def met(a:Any) = { ... }

This is easy to understand.

What sort of advantage the upperbound brings in ?

Thanks!


Solution

  • It allows you to lose less type information than you would by going to Any.

    For example If you have Dog and Cat inherit Animal, this works:

    val maybeDog: Option[Dog] = ???
    
    val pet = maybeDog.getOrElse(Cat())
    

    Because getOrElse has a signature of def getOrElse[B >: A](default: => B): B, it is inferred that B is Animal (as the least upper bound of Cat and Dog), so the static type of val pet is Animal. If it was using Any, the result would require unsafe casting to work further. If it was not introducing a new type parameter altogether, you would be forced to write (maybeDog: Option[Animal]).getOrElse(Cat()) to achieve the same unification.


    Additionally, it restricts the implementation to not do something completely silly. For example, this typechecks:

    def getOrElse[A](option: Option[A])(default: => Any): Any = 42 // Int is Any, so why not?
    

    While this doesn't:

    def getOrElse[A, B >: A](option: Option[A])(default: => B): B = 42 
    

    Because while anything can go as B, that doesn't imply that Int is always a subtype of B.