Type Parameterization

Reading Time: 4 minutes

Introduction

Hello everyone, in this blog we discussed Type Parameterization in Scala. It generally covers generic, types bounds and variance in Scala.

What is type

  • Any class, trait or object is a type
  • Anything defined by type keyword is a type.
  • For example, type A = String

Why we use type parameterization?

Type parameterization allows you to write generic classes and traits. For example, sets are generic and take a type parameter: they are defined as Set[T]. As a result, any particular set instance might be a Set[String], a Set[Int], etc. —but it must be a set of something.

 Generic classes

Generic classes are classes that take a type as a parameter. They are particularly useful for collection classes. Defining generic class generic classes take a type as a parameter within square brackets []. The most common convention is to use the letter A, through any parameter name may be used.

Example for Stack Int Normal class

abstract class IntStack {
  def push(x: Int): IntStack = new IntNonEmptyStack(x, this)
  def isEmpty: Boolean
  def top: Int
  def pop: IntStack
}
class IntEmptyStack extends IntStack {
  def isEmpty = true
  def top = error(“Empty Stack.top”)
  def pop = error("EmptyStack.pop")
}
class IntNonEmptyStack(elem: Int, rest: IntStack) extends IntStack {
def isEmpty = false
def top = elem
def pop = rest
}

Similar example for String Stack

Generic version of Stack

Scala Type Bounds

Upper Bound:
class Parking[A <: Vehicle]

  • The easier type bound to understand is upper type bound ‘<:’. This indicator would be the same as ‘:’ when we create value and we give it a specific type. 
  •  val a: Parking means that “a” must be an instance of Parking or a subtype of Parking.
  • In the type scenario, Parking[A <: Vehicle] means that the A-type must be a type or subtype of Vehicle
trait Thing
trait Vehicle extends Thing
class Car extends Vehicle
class Jeep extends Car
class Coupe extends Car
class Motorcycle extends Vehicle
class Vegetable
class Parking[A<:Vehicle]
new Parking[Motorcycle](new Car)

But if we try to create a Vegetable or Thing Parking, it won’t compile.
Our compiler protects us. If we add these lines to a test, it won’t even compile:
new Parking[Vegetable] should be(a[Parking[_]])
new Parking[Thing] should be(a[Parking[_]])

Lower Bounds

  • On the other hand, we have the lower type bound, ‘>:’, which indicates the opposite of ‘<:’.
  • [A >: Vehicle] will restrict A to supertypes of Vehicle, Vehicle included.
  • Its uses are mainly related to co- and contravariance. Those will be discussed in another article but let’s quickly break down the lower type bound concept. 
  • Let’s start by understanding which type of relationship represents a lower type bound. A >: B means that A must be B or a higher from B, B being the frontier (bound).

For example:

class Parking[A >: Jeep](val place: A)

trait Thing
class Vehicle extends Thing
class Car extends Vehicle
class Jeep extends Car
class Coupe extends Car
class Motorcycle extends Vehicle
class Bicycle extends Vehicle
class Tricycle extends Bicycle

Can we limit Parking to all the subtypes of Vehicles above Tricycle?
class Parking[A >: Bicycle <: Vehicle](val plaza: A)

Now lets take look about Variance

  • Variance is the correlation of subtyping relationships of complex types and the subtyping relationships of their component types.
    Scala supports variance annotations of type parameters of generic classes, to allow them to be covariant, contravariant, or invariant if no annotations are used.
  • The use of variance in the type system allows us to make intuitive connections between complex types, whereas the lack of variance can restrict the reuse of a class abstraction.
    class Dog[+A] // A covariant class
    class Cat[-A] // A contravariant class
    class Animal[A// An invariant class

Covariant

If “S” is subtype of “T” then List[S] is is a subtype of List[T].

  • This kind of Inheritance Relationship between two
    Parameterized Types is known as “Covariant
  • Prefixing Type Parameter with the “+” symbol defines
    Covariance in Scala.

Lets take an example

“As Puppy is a subtype of Dog, Animal[Puppy] is a
subtype of Animal[Dog]. We can use Animal[Puppy] where we require Animal[Dog].” This is known as Scala Covariance. In the below example.

class Animal[+T](val animial:T)
class Dog
class Puppy extends Dog
class AnimalCarer(val dog:Animal[Dog])
object ScalaCovarianceTest {
def main(args: Array[String]) {
val puppy = new Puppy
val dog = new Dog
val puppyAnimal:Animal[Puppy] = new Animal[Puppy](puppy)
val dogAnimal:Animal[Dog] = new Animal[Dog](dog)
val dogCarer = new AnimalCarer(dogAnimal)
val puppyCarer = new AnimalCarer(puppyAnimal)

println("Done.")
}
}

ContraVariance

If “S” is a subtype of “T” then List[T] is is a subtype of List[S].

  • This kind of Inheritance Relationship between
    two Parameterized Types is known
    as “Contravariant
  • Prefixing Type Parameter with “-” symbol
    defines Contravariant in Scala.

Lets take an example

abstract class ContraVariance [-T]{
  def typeName() : Unit
}
class SuperType extends ContraVariance[AnyVal]{
  override def typeName(): Unit = {
    println("SuperType")
  }
}
class SubType extends ContraVariance[Int]{
  override def typeName(): Unit = {
    println("SubType")
  }
}
class TypeCarer{
  def display(t: ContraVariance[Int]){
    t.typeName()
  }
}
object ScalaContravarianceTest {
  def main(args: Array[String]) {
    val superType = new SuperType
    val subType = new SubType
    val typeCarer = new TypeCarer
      typeCarer.display(subType)
    typeCarer.display(superType)
  }
}

In-variance

Generic classes in Scala are invariant by default. This means that they are neither covariant nor contravariant.
Let’s have an example:

class Stack[A] {
  private var elements: List[A] = Nil
  def push(x: A) { elements = x :: elements }
  def peek: A = elements.head
  def pop(): A = {
    val currentTop = peek
    elements = elements.tail
    currentTop
  }
}

Summary

So after reading this you should know what is parameterisation. We discussed why type parameterization comes in picture. What are bounds in Scala and of course Variance in Scala?

References

https://stackoverflow.com/questions/12551674/what-is-meant-by-parameterized-type
https://docs.scala-lang.org/tour/variances.html


Knoldus-blog-footer-image

Leave a Reply