Scala Tutorials Part #26 - Type parameterization


Type parameterization

This is part 26 of the Scala tutorial series. Check here for the full series.

Index

Introduction

Type parameterization or otherwise called as generics combined with Scala’s type system add stability to the code. This concept is not new to Scala. Java added support for generics in J2SE 5.0 which was released in 2004. It equips the programmer with the ability to write code which is more predictable during compile time rather than time. It is heavily used in the collections API of Java and Scala.

ArrayList<String> list=new ArrayList<String>();  
list.add("1");
list.add("2");

Any type within the Java language type system can be encoded within the <>. These are called generics and the class is just defined once using the generics container and can be re-used.

Generics originally came from the world of Functional Programming.

Getting started with an example

We are going to design a Stack data structure from scratch. This is just an example and you would typically use something within collections when it come to actual production code. But for explanation purposes, they do just fine.

class Stack[A] {
}

Here A is a type parameter similar to E used in Java collections API.

Next we will declare two variables which are private to the class.

    //Internal array buffer. Kind of like java array list
private val arrayBuffer = new ArrayBuffer[A]()
//Stack position pointer
private var stack_pointer = -1

We will implement three basic methods push, pop and peek.

    def push(a:A): Unit = {
stack_pointer = stack_pointer + 1
arrayBuffer.append(a)
}

def pop() : Option[A] = {
if(stack_pointer == -1){
None
}
else{
val poppedElement = arrayBuffer(stack_pointer)
stack_pointer = stack_pointer - 1
Some(poppedElement)
}
}

def peek() : Option[A] = {
if(stack_pointer != -1) {
Some(arrayBuffer(stack_pointer))
}
else {
None
}
}

Our final implementation now looks like this.

class Stack[A] {

//Internal array buffer. Kind of like java array list
private val arrayBuffer = new ArrayBuffer[A]()
//Pointer
private var stack_pointer = -1

def push(a:A): Unit = {
stack_pointer = stack_pointer + 1
arrayBuffer.append(a)
}

def pop() : Option[A] = {
if(stack_pointer == -1){
None
}
else{
val poppedElement = arrayBuffer(stack_pointer)
stack_pointer = stack_pointer - 1
Some(poppedElement)
}
}

def peek() : Option[A] = {
if(stack_pointer != -1) {
Some(arrayBuffer(stack_pointer))
}
else {
None
}
}

}

Using this data structure is pretty simple.

  val stack = new Stack[Int]

stack.push(10)
stack.push(20)
stack.push(30)

println(stack.pop())
println(stack.peek())

Using custom class types

Since the type A can be used to encode almost any type, lets try some of our custom classes.

//Root trait
trait Car

//Types extending from the Car trait
class Toyota extends Car
class Hyundai extends Car

We can then use these classes in our stack data structure.

  val t = new Toyota
val h = new Hyundai

val stack = new Stack[Car]

stack.push(t)
stack.push(h)

Multiple type parameters

The Java language has an interface called Pair which goes as follows,

public interface Pair<K, V> {
public K getKey();
public V getValue();
}

It has two generic parameters. This is used in collections such as HashMap. Scala’s counterpart is not that different.

trait Pair[A,B] {
def getKey : A
def getValue : B
}

The [] is equivalent to <> in terms of syntactic sugar as we saw in examples above.

Decomposing types with Generic methods

In part 23 we saw an example in which we decomposed a higher type Any into its sub types.

val typeTest : Any = "String"

typeTest match {
case i : Int => println("Integer type")
case d : Double => println("Double type")
case f : Float => println("Float type")
case s : String => println("String type")
case _ : BigDecimal => println("Big decimal type")
case _ => println("Unknown type")
}

We explicitly gave the type ascription to the variable typeTest. This gets translated to type casting and it is not necessarily a good way to do this.

A better and a type safe way to accomplish this is by using a generic type signature. Let’s create an empty method which operates on generic types.

def identifyType[A](value:A)  = {
}

The [A] after the method name is necessary to indicate that the method operates on generic types. Next we will do the same pattern matching inside the method.

  def identifyType[A](value:A)  = {

value match {
case i : Int => println("Integer type")
case d : Double => println("Double type")
case f : Float => println("Float type")
case s : String => println("String type")
case _ : BigDecimal => println("Big decimal type")
case _ => println("Unknown type")
}

}

Now we can call this without explicit type annotation, since it is taken care by the compiler.

  //String
identifyType("Testing")
//Integer
identifyType(10)
//Double
identifyType(20.0)

case class Box(name:String)
val box = Box("test")
//Custom type - No explicit annotation
identifyType(box)

Prints out the following.

String type
Integer type
Double type
Unknown type

Conclusion

Generics are a powerful way of abstraction and writing type safe code which is more compile time reliant rather than run time. We have merely scratched the surface of what is possible with them in Scala and this article has been more like an introduction to its syntax. I have deliberately left out advanced topics such as type variance, structural typing(duck typing) which will be explained in later tutorials.

The Stack data structure above is partially derived from the Scala lang’s official documentation guide on generics.


Tagged Under


Scala


Search this website...




Keeping up with blogs...

I blog occasionally and mostly write about Software engineering and the likes. If you are interested in keeping up with new blog posts, you should follow me on twitter where I usually tweet when I publish them. You can also use the RSS feed , or even subscribe via email below.

Feedio Subscribe


Share & Like

If you like this post, then you can either share/discuss/vote up on the below sites.



Thoughts ...

Please feel free to share your comments below for discussion

Blog comments powered by Disqus