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
