Wednesday, December 7, 2011

Learning Scala : upper bound "<:" and lower bound ">:" for laypeople

Authored by Win Myo Htet


Scala has a very rich type system. One aspect of the type system is variance, which is annotated by "+" or "-" or " " (e.g. List[+A]). From that, we also have lower bound and upper bound. We are not going to get into the detail of it. We will only try to understand it from the object oriented analogy.  Generally, lower bound, ">:", can be seen as [Parent >: Child] format and upper bound, "<:", as [Child<:Parent]. They are there for the proper usage of the Scala generic. A code snippet is worth a thousand words. So, here is the code:

abstract class Animal (animalType:String)

class HasFourLegs(animalType:String) extends Animal(animalType){
  def move=println(this+" walking on four legs")
}

class HasTwoLegs(animalType:String) extends Animal(animalType){
  def move=println(this+" walking on Two legs")
}

case class Dog(animalType:String) extends HasFourLegs(animalType)
case class Ostrich(animalType:String) extends HasTwoLegs(animalType)

  def moveOn4legs[T<:HasFourLegs](animal:T)=  animal.move
  val dog = Dog("dog")
  val ostrich=Ostrich("ostrich")
  moveOn4legs(dog)
  /*
  moveOn4legs(ostrich)
  error: inferred type arguments [this.Ostrich] do not conform to method moveOn4legs's type parameter bounds [T <: this.HasFourLegs]
  moveOn4legs(ostrich)
  ^
  */
  println
  
class AnimalMovement [+T]{
  def movement[U>:T](animal:U)=println(animal+" walking on Two legs!!!")
}

  val moveLikeTwoLegs=new AnimalMovement[HasTwoLegs]()
  moveLikeTwoLegs.movement(ostrich)
  moveLikeTwoLegs.movement(dog)

We have two kinds of animals: 4 legs and 2 legs. From there, we have a Dog and an Ostrich. We then have a method, moveOn4legs, with upper bound parameterized as moveOn4legs[T<:HasFourLegs], thus restricting the  paramter type to be the Child of HasFourLegs class. We can only use this method for Dog which extends HasFourLegs. We get compile time error when we use Ostrich which does not extends HasFourLegs.

Now we get to the AnimalMovement class with [+T], which is covariance. ([-T] is contravariance and [T] is invariant). Inside it, we have a parameterized method with lower bound,  movement[U>:T](animal:U). We create an object, moveLikeTwoLegs, out of it with the type HasTwoLegs. The movement method is then invoked with both dog and ostrich. Here is the output of this whole code snippet.

aunndroid@ubuntu:/host/linux/learning_scala/notes$ scala upper_lower_Bound.scala 
Dog(dog) walking on four legs

Ostrich(ostrich) walking on Two legs!!!
Dog(dog) walking on Two legs!!!


From, the print out of the last two lines, we see that lower bound let us use dog, an object of DOG, which is not a Child of HasTwoLegs but of HasFourLegs, which shares the same Parent Animal.

Of course, proper usage of the terms should be supertype for Parent and subtype for Child but we just want to grasp the concept of upper bound "<:" and lower bound ">:" only.


Authored by Win Myo Htet

No comments:

Post a Comment