Why do we talk about type system in Scala? There are several reasons:

- The first one is that Scala allows us to define parameter’s type.
- We can make our class/function to be fit for different types. (generic programming or parametric polymorphism)
- Sometimes, Scala helps us to do type inference.
- We can limit the class/function’s type scope

I think the most important point is to implement generic programming in Scala. So first we talk about parametric polymorphism.

# Parametric polymorphism

We use one example to explain it.

def drop1[A](l: List[A]) = l.tail drop1(List(1,2,3)) drop1(List("a","b","c"))

Obviously, this function can be applied in Int, String and etc. We don’t need to write down two or more functions to satisfy different types; we only use type parameter to give a generic method to implement the function. It saves our time.

And then we talk about in some conditions, we don’t need to point out its type. Scala helps us to infer its type.

# Type inference

Of course, we use codes to express how does Scala infer the type.

def id[T](x: T) = x val x = id(322) // Int = 322 val x = id("hey") // java.lang.String = hey val x = id(Array(1,3,4)) // Array[Int] = Array[1,3,4]

As you seen, it is quite easy for us to understand the return type; we don’t need to point out its type by ourselves.

It seems type system is so easy, right? No. Another problem is coming. If T” is a subclass of T, what is relationship between Container[T] and Container[T”]? Let’s talk about variance to express the relationship.

# Variance

If T” is a subclass of T,

- covariant [+T] C[T”] is a subclass of C[T]
- contravariant [-T] C[T] is a subclass of C[T”]
- invariant [T] C[T] and C[T”] are not related

Here, I give each an example.

## covariant

class Covariant[+A] val cv: Covariant[AnyRef] = new Covariant[String]

## contravariant

class Contravariant[-A] val cv: Contravariant[String] = new Contravariant[AnyRef]

## invariant

class Invariant[A] val cv: Invariant[String] = new Invariant[String]

That’s all? oh, no. Scala allows us to restrict polymorphic variables by bounds, which express subtype relationships.

## bounds

bounds can be separated to upper bounds and lower bounds.

### upper bounds

upper bounds mean a type is another type’s sub-class, using extends(Java). For example:

T extends Test // Java [T <: Test] // Scala def pr(list: List[_ <: Any]) { list.foreach(println) }

### lower bounds

lower bounds mean a type is another type’s super-class, using super(Java). For example:

T super Test // Java [T >: Test] // Scala def append[T >: String](buf: ListBuffer[T]) = { buf.append("hi") }

In fact, there is another kind of bounds, named view bounds.

### view bounds

To be honest, I don’t know why its name is “view bounds”, here I only talk what I know. “view bounds” is related to implicit class; it demand a function exists for the given type. We can use “<%” to specify a view bound. Here is the example:

implicit def strToInt(x: String) = x.toInt class Container[A <% Int]{def addInt(x: A) = 123 + x} (new Container[String]).addInt("123") // Int = 246 (new Container[Int]).addInt(123) // Int = 246 (new Container[Float]).addInt(123.2F) // error: no implicit view available from Float=>Int

There are still other type of bounds. I will add them in the future when I really understand them.

That’s all for type system. It is quite useful when we need to implement generic programming.