We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies.

We use cookies and other tracking technologies to improve your browsing experience on our site, analyze site traffic, and understand where our audience is coming from. To find out more, please read our privacy policy.

By choosing 'I Accept', you consent to our use of cookies and other tracking technologies. Less

We use cookies and other tracking technologies... More

# Login or registerto boost this post!

Show some love to the author of this blog by giving their post some rocket fuel π.

# Login or register to start working on this issue!

Engineers who find a new job through Functional Works average a 15% increase in salary π

# Understand Scala variances by building restaurants

Luca Florio 2 September, 2019 | 6 min read

Understand type variance is not fundamental to write Scala code. It's more or less a year that I'm using Scala for my day-to-day job, and honestly, I never had to worry much about it. However, I think it is an interesting "advanced" topic, so I started to study it. It is not immediate to grasp, but with the right example, it would be a little bit easier to understand. Let me try using a food-based analogy...

## What is type variance

First of all, we have to define what type variance is. When you develop in an Object-Oriented language, you can define complex types. That means that a type may be parametrized using another type (component type). Think of `List` for example. You cannot define a `List` without specifying which types will be inside the list. You do it by putting the type contained in the list inside square brackets: `List[String]`. When you define a complex type, you can specify how it will vary its subtype relationship according to the relation between the component type and its subtypes. Ok, sounds like a mess... Let's get a little practical.

## Building a restaurants empire

Our goal is to build an empire of restaurants. We want generic and specialised restaurants. Every restaurant we will open needs a menu composed of different recipes, and a (possibly) starred chef. The recipes can be composed of different kind of food (fish, meat, white meat, vegetables, etc.), while the chef we hire has to be able to cook that kind of food. This is our model. Now it's coding time!

## Different types of food

For our food-based example, we start by defining the `Trait Food`, providing just the name of the food.

``````trait Food {

def name: String

}
``````

Then we can create `Meat` and `Vegetable`, that are subclasses of `Food`.

``````class Meat(val name: String) extends Food
``````
``````class Vegetable(val name: String) extends Food
``````

In the end, we define a `WhiteMeat` class that is a subclass of `Meat`.

``````class WhiteMeat(override val name: String) extends Meat(name)
``````

Sounds reasonable right? So we have this hierarchy of types.

We can create some food instances of various type. They will be the ingredients of the recipes we are going to serve in our restaurants.

``````// Food <- Meat
val beef = new Meat("beef")

// Food <- Meat <- WhiteMeat
val chicken = new WhiteMeat("chicken")
val turkey = new WhiteMeat("turkey")

// Food <- Vegetable
val carrot = new Vegetable("carrot")
val pumpkin = new Vegetable("pumpkin")
``````

## Recipe, a covariant type

Let's define the covariant type `Recipe`. It takes a component type that expresses the base food for the recipe - that is, a recipe based on meat, vegetable, etc.

``````trait Recipe[+A] {

def name: String

def ingredients: List[A]

}
``````

The `Recipe` has a name and a list of ingredients. The list of ingredients has the same type of `Recipe`. To express that the `Recipe` is covariant in its type `A`, we write it as `Recipe[+A]`. The generic recipe is based on every kind of food, the meat recipe is based on meat, and a white meat recipe has just white meat in its list of ingredients.

``````case class GenericRecipe(ingredients: List[Food]) extends Recipe[Food] {

def name: String = s"Generic recipe based on \${ingredients.map(_.name)}"

}
``````
``````case class MeatRecipe(ingredients: List[Meat]) extends Recipe[Meat] {

def name: String = s"Meat recipe based on \${ingredients.map(_.name)}"

}
``````
``````case class WhiteMeatRecipe(ingredients: List[WhiteMeat]) extends Recipe[WhiteMeat] {

def name: String = s"Meat recipe based on \${ingredients.map(_.name)}"

}
``````

A type is covariant if it follows the same relationship of subtypes of its component type. This means that `Recipe` follows the same subtype relationship of its component Food.

Let's define some recipes that will be part of different menus.

``````// Recipe[Food]: Based on Meat or Vegetable
val mixRecipe = new GenericRecipe(List(chicken, carrot, beef, pumpkin))
// Recipe[Food] <- Recipe[Meat]: Based on any kind of Meat
val meatRecipe = new MeatRecipe(List(beef, turkey))
// Recipe[Food] <- Recipe[Meat] <- Recipe[WhiteMeat]: Based only on WhiteMeat
val whiteMeatRecipe = new WhiteMeatRecipe(List(chicken, turkey))
``````

## Chef, a contravariant type

We defined some recipes, but we need a chef to cook them. This gives us the chance to talk about contravariance. A type is contravariant if it follows an inverse relationship of subtypes of its component type. Let's define our complex type `Chef`, that is contravariant in the component type. The component type will be the food that the chef can cook.

``````trait Chef[-A] {

def specialization: String

def cook(recipe: Recipe[A]): String
}
``````

A `Chef` has a specialisation and a method to cook a recipe based on a specific food. We express that it is contravariant writing it as `Chef[-A]`. Now we can create a chef able to cook generic food, a chef able to cook meat and a chef specialised on white meat.

``````class GenericChef extends Chef[Food] {

val specialization = "All food"

override def cook(recipe: Recipe[Food]): String = s"I made a \${recipe.name}"
}
``````
``````class MeatChef extends Chef[Meat] {

val specialization = "Meat"

override def cook(recipe: Recipe[Meat]): String = s"I made a \${recipe.name}"
}
``````
``````class WhiteMeatChef extends Chef[WhiteMeat] {

override val specialization = "White meat"

def cook(recipe: Recipe[WhiteMeat]): String = s"I made a \${recipe.name}"
}
``````

Since `Chef` is contravariant, `Chef[Food]` is a subclass of `Chef[Meat]` that is a subclass of `Chef[WhiteMeat]`. This means that the relationship between subtypes is the inverse of its component type Food.

Ok, we can now define different chef with various specialization to hire in our restaurants.

``````// Chef[WhiteMeat]: Can cook only WhiteMeat
val giuseppe = new WhiteMeatChef
giuseppe.cook(whiteMeatRecipe)

// Chef[WhiteMeat] <- Chef[Meat]: Can cook only Meat
val alfredo = new MeatChef
alfredo.cook(meatRecipe)
alfredo.cook(whiteMeatRecipe)

// Chef[WhiteMeat]<- Chef[Meat] <- Chef[Food]: Can cook any Food
val mario = new GenericChef
mario.cook(mixRecipe)
mario.cook(meatRecipe)
mario.cook(whiteMeatRecipe)
``````

## Restaurant, where things come together

We have recipes, we have chefs, now we need a restaurant where the chef can cook a menu of recipes.

``````trait Restaurant[A] {

def chef: Chef[A]

}
``````

We are not interested in the subtype relationship between restaurants, so we can define it as invariant. An invariant type does not follow the relationship between the subtypes of the component type. In other words, `Restaurant[Food]` is not a subclass or superclass of `Restaurant[Meat]`. They are simply unrelated. We will have a `GenericRestaurant`, where you can eat different type of food. The `MeatRestaurant` is specialised in meat-based dished and the `WhiteMeatRestaurant` is specialised only in dishes based on white meat. Every restaurant to be instantiated needs a menu, that is a list of recipes, and a chef able to cook the recipes in the menu. Here is where the subtype relationship of `Recipe` and `Chef` comes into play.

``````case class GenericRestaurant(menu: List[Recipe[Food]], chef: Chef[Food]) extends Restaurant[Food]
``````
``````case class MeatRestaurant(menu: List[Recipe[Meat]], chef: Chef[Meat]) extends Restaurant[Meat]
``````
``````case class WhiteMeatRestaurant(menu: List[Recipe[WhiteMeat]], chef: Chef[WhiteMeat]) extends Restaurant[WhiteMeat]
``````

Let's start defining some generic restaurants. In a generic restaurant, the menu is composed of recipes of various type of food. Since `Recipe` is covariant, a `GenericRecipe` is a superclass of `MeatRecipe` and `WhiteMeatRecipe`, so I can pass them to my `GenericRestaurant` instance. The thing is different for the chef. If the Restaurant requires a chef that can cook generic food, I cannot put in it a chef able to cook only a specific one. The class `Chef` is covariant, so `GenericChef` is a subclass of `MeatChef` that is a subclass of `WhiteMeatChef`. This implies that I cannot pass to my instance anything different from `GenericChef`.

``````val allFood = new GenericRestaurant(List(mixRecipe), mario)
val foodParadise = new GenericRestaurant(List(meatRecipe), mario)
val superFood = new GenericRestaurant(List(whiteMeatRecipe), mario)
``````

The same goes for `MeatRestaurant` and `WhiteMeatRestaurant`. I can pass to the instance only a menu composed of more specific recipes then the required one, but chefs that can cook food more generic than the required one.

``````val meat4All = new MeatRestaurant(List(meatRecipe), alfredo)
val meetMyMeat = new MeatRestaurant(List(whiteMeatRecipe), mario)
``````
``````val notOnlyChicken = new WhiteMeatRestaurant(List(whiteMeatRecipe), giuseppe)
val whiteIsGood = new WhiteMeatRestaurant(List(whiteMeatRecipe), alfredo)
val wingsLovers = new WhiteMeatRestaurant(List(whiteMeatRecipe), mario)
``````

That's it, our empire of restaurants is ready to make tons of money!

## Conclusion

Ok guys, in this story I did my best to explain type variances in Scala. It is an advanced topic, but it is worth to know just out of curiosity. I hope that the restaurant example can be of help to make it more understandable. If something is not clear, or if I wrote something wrong (I'm still learning!) don't hesitate to leave a comment! :wink:

See you! :rocket:

Originally published on www.freecodecamp.org