Scala Type Bounds
Tags: scala, java, jvm, functional
Published: 2020-08-10 13:16:36


Working with types in Scala can be challenging in many ways. The deeper you dig into the complexity of this subject, the more difficult is to find the right path. In this short post, I will be explaining some of the most fundamental of Scala type system — the type bounds. Enjoy!

## What is type bound?

A type bound is nothing more than a mechanism to add restrictions to the type parameters. When passing a type to let’s say a class, we can limit to what types we want to allow. This mechanism allows for greater control over our code and hopefully making it more robust.

There are 3 distinct type bounds available in Scala — upper bound, lower bound and upper and lower combined. The chapters below explain how to use each of them.

## Upper type bound

An upper type bound constraints a type `A` to be a **subtype** of `B`. The Scala notation looks like this:
```
    class SomeClass[A <: B]
```
The example above defines the type of `A` as a subtype of `B`. To understand it better, let's use some better example:
```
    trait Eatable

    class Vegetable extends Eatable
    class Meat extends Eatable

    class Potato extends Vegetable
    class Beef extends Meat
    class Orange extends Eatable

    class Vegan[A <: Vegetable]
    class Carnivore[A <: Meat]
    class Food[A <: Eatable]

    val vegan = new Vegan[Potato] // works fine
    val mixed = new Carnivore[Potato] // error - type argument doesn't conform to type parameter bound
    val all = new Food[Beef] // all good
```
As seen on a few examples above, the mechanism is pretty easy to use. The class `Vegan`, requires its type parameter to a subtype of `Vegetable`, which is exactly what `Potato` is. On the other hand, class `Carnivore` requires its parameter type to be a subtype of `Meat`, which `Potato` isn't hence the error. The `Food` class accepts any class that extends `Eatable` trait - a `Beef` is one of them.

## Lower type bound

A lower type bounds works as one would expect — it limits the type `A` to be a **supertype** of `B`. The notation looks like this:
```
    class OtherClass[A >:  B]
```
The notation is similar to previous bound — only the less than sign is replaced with greater than sign. Let’s use the classes from previous examples to see the lower type bound in practice:
```
    trait Eatable

    class Vegetable extends Eatable
    class Meat extends Eatable

    class Potato extends Vegetable
    class Beef extends Meat
    class Orange extends Eatable

    class Stew[A >: Potato]
    class BBQ[A >: Beef]
    class Juice[A >: Orange]

    val stew = new Stew[Vegetable] // works fine
    val BBQ = new BBQ[Meat] // fine as well
    val juice = new Juice[Potato] // error
```
The lower type bound is similarly easy to use. We specify three more concrete classes: `Stew`, `BBQ`, and `Juice`. `Stew` requires its type parameter to a supertype of `Potato`, for `BBQ` the type parameter should be a supertype of `Beef` and for `Juice` - supertype of `Orange`. First to instantiations work fine, but the last one fails - the `Potato` is by no means a supertype of `Orange`, therefore an error is thrown.

## Lower and upper type bound

Last example is actually a combination of previous two. Scala allows to define both lower and upper bounds in the same time. The notation for this looks like this:
```
    class LastClass[A <: B >: C]
```
In this example, the type `A`, has to be a subtype of `B`, while also being a supertype of `C`. As complicated as it may sound, it will be easier to understand using our 'food' examples:
```
    trait Eatable

    class Vegetable extends Eatable
    class Meat extends Eatable

    class Potato extends Vegetable
    class Beef extends Meat
    class Orange extends Eatable

    class MarisPiper extends Potato
    class Wagyu extends Beef
    class Curacao extends Orange

    class RedMeat[A <: Meat >: Wagyu]
    class RootVegetable[A <: Vegetable >: MarisPiper]

    val stew = new RedMeat[Beef] // all good
    val BBQ = new RootVegetable[Potato] // works fine
    val juice = new RootVegetable[MarisPiper] // error
```
This example should be fairly self-explanatory — we have two classes that use both upper and lower type bounds for their type parameters: `RedMeat` and `RootVegetable`. From the classes defined, the `Beef` works for the first one, and `Potato` for the second one. The `Meat` doesn't meet the upper type bound and `Wagyu` the lower type bound for `RedMeat` class type parameters.

## Summary

I hope you have found this post useful. If so, don’t hesitate to like or share this post. Additionally, you can follow me on my social media if you fancy so 🙂

{% twitter 1305856520098373632 %}