> For the complete documentation index, see [llms.txt](https://harmeetsingh.gitbook.io/scala-type-system/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://harmeetsingh.gitbook.io/scala-type-system/phase-i/chapter-9-type-constraints.md).

# Chapter 9: Type Constraints

In Scala, when we define a type parameter such as `[T]`, we can usually pass any type. However, sometimes we want to restrict the allowed types. In such cases, **type constraints** come into the picture.

Let's start with an example:&#x20;

```scala
trait Avenger
case class CaptainMarvel() extends Avenger
case class CaptainAmerica() extends Avenger
case class IronMan() extends Avenger
case class Hulk() extends Avenger

def saveTheDay[T](superHero : T, city: String) = {
	// fight villains and save the city
}

saveTheDay[IronMan](IronMan, “New York”) // compiles and run successfully
saveTheDay[Hulk](Hulk, “Virginia”) // compiles and run successfully
```

In this example, we call the `saveTheDay` method by passing a superhero object to save a city. But what if we pass a supervillain instead? Does that make sense?

Let's try:

```scala
case class Loki() 
saveTheDay[Loki](Loki, “New York”) // compiles and run successfully
```

The code compiles successfully, but logically it does not make sense. Loki is not an Avenger, so he should not be allowed to save the day.

This is our **first problem**: there is no restriction on the type that can be passed to `saveTheDay`. Since `T` represents any type, the compiler allows us to pass an object of any class.

> **Note:** Loki saving the city is not very realistic. He is much more likely to destroy it!

Now let's look at the **second problem**.

Suppose all Avengers have one common goal: to stop Thanos. For that, we define a common behavior in the `Avenger` trait:

```scala
trait Avenger {
	def stopTheThanos(avengers: Avenger) = ???
}
```

Now we define a generic method:

```scala
def endGame[T](avenger : T) = avengers.stopTheThanos(avenger)
// Compilation error: stopThanos is not a member of T
```

The method does not compile because `T` can represent any type. The compiler has no guarantee that every possible `T` defines a `stopThanos` method.

These two problems are common when working with parameterized types:

1. We want to restrict which types can be passed to a method.
2. We want to access methods that are available only on a specific type hierarchy.

Scala solves both problems using **type bounds**.

There are two kinds of type bounds:

* **Upper Type Bounds (`<:`)**
* **Lower Type Bounds (`>:`)**

The idea behind type bounds is simple: they restrict a type parameter to a particular part of the type hierarchy. This gives us stronger compile-time guarantees while still allowing our code to remain generic.

### **Upper Type Bound ( <: )**

Let's solve the first problem.

Our original method accepts any type:

```scala
def saveTheDay[T](superHero : T, city: String) = {
	// fight villains and save the city
}
```

To restrict the allowed types, we can introduce an **upper type bound**:

```scala
def saveTheDay[T <: Avengers](superHero : T, city: String) = {
	// fights with villains and save the day of the city
}
```

The expression:

```
T <: Avenger
```

means:

> **`T` must be either `Avenger` itself or one of its subtypes.**

Now only Avengers can be passed to the method:

```scala
saveTheDay[IronMan](IronMan, “New York”) // compiles and run successfully
saveTheDay[Hulk](Hulk, “Virginia”) // compiles and run successfully

saveTheDay[Loki](Loki, “New York”) // compile fails

error: type arguments [Loki] do not conform to method saveTheDay's type parameter bounds [T <: Avengers]
       saveTheDay[Loki](Loki, "New York")
```

With the help of an **upper type bound**, we successfully restrict the accepted types and gain compile-time safety. Without this constraint, invalid objects could be passed into the method, leading to incorrect program behavior.

Now let's solve our second problem.

```scala
trait Avenger {
	def stopTheThanos(avenger: Avenger) = ???
}

def endGame[T](avenger : T) = ???
```

Again, the compiler does not know that `T` belongs to the `Avenger` hierarchy.

We can solve this by applying the same upper type bound:

```scala
def endGame[T <: Avenger](avenger : T) = avengers.stopTheThanos(avengers)
// code compiles successfully. 
```

Now the compiler knows that every value of type `T` is at least an `Avenger`. Therefore, it allows us to call every method defined in the `Avenger` trait.

#### How Does This Work After Type Erasure?

In the [7th chapter](/scala-type-system/phase-i/chapter-7-type-erasure.md), we learned that type erasure removes generic type information during compilation.

Normally, an unconstrained type parameter such as:

```scala
def method[T](value: T)
```

is erased to:

```scala
def method(value: Object)
```

(or `AnyRef` in Scala terms).

However, when we introduce an upper type bound:

```scala
def endGame[T <: Avenger](avenger: T)
```

the compiler erases `T` to its upper bound instead of `Object`.

Conceptually, the generated code behaves as if it were:

```scala
def endGame(avenger: Avenger) =
  avenger.stopThanos()
```

Since the compiler now knows that every argument is an `Avenger`, it can safely allow calls to all methods defined by the `Avenger` trait.

This is the purpose of an **upper type bound**—it not only restricts the accepted types but also gives the compiler additional information about the operations that can safely be performed on those types.

If you are familiar with Java generics, the equivalent syntax is:

```java
<T extends Avenger>
```

### Lower Type Bounds (`>:`)

IA **lower type bound** is declared using the `>:` operator.

For example:

```
def wallOfHeroes[T >: Avenger](heroes: T) = ???
```

The expression:

```
T >: Avenger
```

means:

> **`T` must be either `Avenger` itself or one of its supertypes.**

Let's consider the following hierarchy:

```scala
trait Heroes
class DcHeroes extends Heroes
class MarvelHeroes extends Heroes

class Avenger extends MarvelHeroes
class JusticeLeague extends DcHeroes

case class CaptainMarvel() extends Avenger
case class CaptainAmerica() extends Avenger
```

Now suppose we define the following method:

```scala
def wallOfHeroes[T >: Avenger](heroes: T) = ???
```

This method accepts `Avenger` and all of its supertypes.

```scala
wallOfHeroes[Avenger](new Avenger) // Compiles successfully
wallOfHeroes[MarvelHeroes](new MarvelHeroes) // Compiles successfully
wallOfHeroes[Heroes](new Heroes {}) // Compiles successfully
```

However, the following code does **not** compile:

```scala
wallOfHeroes[CaptainMarvel](CaptainMarvel()) // Compiles successfully
```

**Why?**

Because `CaptainMarvel` is a **subtype** of `Avenger`, whereas a lower type bound requires the type parameter to be `Avenger` itself or one of its **supertypes**.

If you are familiar with Java generics, the equivalent syntax is:

```
<? super Avenger>
```

#### Think Like the Compiler

When working with type bounds, it is useful to **think like the compiler**.

The compiler only knows what is guaranteed by the declared type constraints. It cannot assume anything beyond those guarantees.

Let's look at a few examples.

```scala
wallOfTeam[DcHeroes](new DcHeroes) // Compilation fails
```

This code does **not** compile. Although `DcHeroes` is a subtype of `Heroes`, it is **not** a supertype of `Avenger`. The hierarchy looks like this:

```shellscript
Heroes 
├── MarvelHeroes 
│   └── Avenger 
│        └── CaptainMarvel 
│        └── CaptainAmerica 
└── DcHeroes 
    └── JusticeLeague
```

`DcHeroes` and `MarvelHeroes` are sibling types. Since `DcHeroes` is not part of the `Avenger` hierarchy, it does not satisfy the lower bound.

Now consider another example:

```scala
wallOfTeam[Heroes](new DcHeroes) // Compiles and run successfully.
```

Surprisingly, this code **does** compile successfully.

At first glance, this may seem confusing.

The compiler checks two things independently:

1. Does the type argument satisfy the lower bound?
2. Can the value be assigned to that type?

The type argument is `Heroes`. Since `Heroes` is a supertype of `Avenger`, it satisfies:

```scala
T >: Avenger
```

Next, the compiler checks the method argument:

```scala
new DcHeroes
```

Since `DcHeroes` is a subtype of `Heroes`, it can be assigned to a variable of type `Heroes`. Therefore, the call is perfectly valid. Now consider the following code:

```scala
wallOfTeam(new DcHeroes) // Compile and run successfully
```

This also compiles successfully.

> Why?

Because type inference comes into the picture. The compiler automatically infers a suitable type argument. Conceptually, it behaves as if you had written:

```scala
wallOfTeam[Heroes](new DcHeroes)
```

This satisfies the lower bound, so the program compiles successfully.

#### Type Erasure and Lower Type Bounds

Unlike upper type bounds, lower type bounds do **not** provide the compiler with additional information about the available methods.

For example:

```scala
def wallOfHeroes[T >: Avenger](heroes: T) = ???
```

After type erasure, the compiler treats `T` as `Object` (or `AnyRef` in Scala terms), because there is no single upper bound that provides additional behavior. As a result, inside the method you can safely call only the methods that are available on `AnyRef`.

This is why lower type bounds are rarely used to access behavior. Instead, they are primarily used together with **variance**, where they play an important role in maintaining type safety.

We will explore this in detail in the next chapter.

### Context-Bounds

Despite their name, **context bounds** are **not** related to upper type bounds (`<:`) or lower type bounds (`>:`). They solve a completely different problem and were introduced in Scala 2.8.

So, why are they included in this chapter?

Simply because they also use the word **bound**.

In the previous chapter, we implemented **type classes** to achieve **ad-hoc polymorphism**. As part of that implementation, we created a generic method that accepted an implicit type class instance:

```scala
def behavior[T](animal : T)(implicit val animalBehavior: AnimalBehaviors[T]) = {
	animalBehavior.behavior(animal)
}
```

Although this approach works well, method signatures can become verbose when a method requires one or more implicit parameters. Since type classes are widely used in Scala libraries such as **Scalaz** and **Cats**, Scala provides a more concise syntax called a **context bound**.

Using a context bound, the same method can be written as:

```scala
def behavior[T : AnimalBehaviors](animal : T) = {
	implicitly[AnimalBehaviors].behavior(animal)
}
```

As you can see, we defined `[T: AnimalBehaviors]`, this doesn’t mean mathematical equals **( : )** like in our previous **(<: , >: )**&#x62;ounds examples, here **( : )** means that the method defining an implicit parameter `AnimalBehaviors` of type **T** as we defined in the first context bound example. With the help of `implicitly[AnimalBehaviors]`, it will find the implicit object of `AnimalBehaviors[T]` and call its `behavior` method.&#x20;

In the simplest way, you can think like that the method signature:&#x20;

```scala
def behavior[T : AnimalBehaviors](animal : T) = {
	implicitly[AnimalBehaviors].behavior(animal)
}
```

The expression:

```
[T: AnimalBehavior]
```

means:

> **There must be an implicit value of type `AnimalBehavior[T]` available in the current scope.**

Unlike `<:` and `>:`, the colon (`:`) used in a context bound is **not** a mathematical operator. Instead, it is simply shorthand for an implicit parameter. The compiler expands the method into something conceptually similar to:

```scala
def behavior[T](animal : T)(implicit val animalBehavior: AnimalBehaviors[T]) = {
	animalBehavior.behavior(animal)
}
```

This means that context bounds do not introduce a new language feature—they simply provide a more compact syntax for methods that require implicit type class instances.

### Summary

Scala provides three different kinds of bounds, each serving a different purpose:

| Bound                | Syntax   | Purpose                                                     |
| -------------------- | -------- | ----------------------------------------------------------- |
| **Upper Type Bound** | `T <: A` | Restricts `T` to `A` or one of its subtypes.                |
| **Lower Type Bound** | `T >: A` | Restricts `T` to `A` or one of its supertypes.              |
| **Context Bound**    | `T: TC`  | Requires an implicit value of type `TC[T]` to be available. |

Although they all use the word **bound**, they solve different problems:

* **Upper and lower type bounds** restrict a type parameter within a type hierarchy.
* **Context bounds** require implicit evidence, most commonly in the form of a type class instance.

Understanding these three kinds of bounds is essential for writing generic, type-safe Scala code.

In the next chapter, we will explore **variance**, one of the most powerful—and often confusing—features of Scala's type system. Fortunately, the concepts we learned in this chapter will make variance much easier to understand.
