> 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-8-type-classes-and-add-hoc-polymorphism.md).

# Chapter 8: Type Classes and Ad-hoc Polymorphism

### **Ad-hoc Polymorphism**

**Ad-hoc polymorphism** allows the same operation to have different implementations depending on the type of its arguments. In object-oriented programming, this is commonly achieved through **method overloading**. In functional programming, the same concept is typically implemented using **type classes**.

We have already seen **parametric polymorphism**, where a single generic implementation works for many different types. However, what if we want the same operation to behave differently for different types?

This is where **ad-hoc polymorphism** comes into the picture.

In traditional object-oriented programming, data and behavior are usually defined together. Consider the following example:

```scala
// Polymorphism
trait Animal(name : String) {
	def behaviour(): String
}

class Dog(name: String) extends Animal(name) {
	override def behaviour() = ???
}

class Cat(name: String) extends Animal(name) {
	override def behaviour() = ???
}
```

In this design, each class contains both its data (`name`) and its behavior (`behavior`).

With ad-hoc polymorphism, we separate **data** from **behavior**. Instead of placing the behavior inside the data type itself, we define it independently and associate it with the type when needed.

One common way to achieve this in Scala is by using **type classes**.

### Type Classes

The concept of **type classes** originated in Haskell as a mechanism for implementing ad-hoc polymorphism.

Scala does not provide built-in language support for type classes like Haskell. Instead, they can be implemented using existing Scala features such as:

* Traits
* Generic type parameters
* Implicits

A common approach to designing a type class consists of three simple steps:

1. Define a generic abstract type.
2. Provide implementations for specific types.
3. Create a generic API that delegates to the appropriate implementation.

Let's see how this works.

**Step 1: Define the Type Class**

First, define a generic abstract type. In Scala, a trait is a natural choice.

```scala
class Dog(name : String)
class Cat(name: String)

trait AnimalBehaviors[T] {
	def behavior (animal: T)
}
```

The trait describes the behavior without specifying how it should be implemented.

**Step 2: Provide Implementations**

Next, create an implementation of the type class for each concrete type.

```scala
class DogBehaviour extends AnimalBehaviors[Dog] {
	def behavior (dog: Dog) = ???
}

class CatBehavior extends AnimalBehaviors[Cat] {
	def behavior (cat: Cat) = ???
}

implicit val dogBehaviour = new DogBehaviour
implicit val catBehaviour = new CatBehaviour
```

Each implementation describes how a particular type behaves.

**Notice** that the implementations are declared as `implicit` values. This allows the compiler to automatically select the appropriate implementation based on the type being used.

**Step 3: Create a Generic API**

Finally, expose a generic method that delegates to the appropriate type class implementation.

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

behavior[Dog](Dog(“Jimmy”)) // call DogBehaviour implemnetation
behavior[Cat](Cat(“Sweety”)) // call CatBehavior implemnetation
```

The `behavior` method is completely generic. It does not know anything about `Dog` or `Cat`.

When the method is called, the compiler searches for an implicit value of type `AnimalBehavior[T]`.

For example:

* `behavior(Dog("Jimmy"))` uses `DogBehavior`.
* `behavior(Cat("Sweety"))` uses `CatBehavior`.

The correct implementation is selected automatically at compile time.<br>

#### Why Use Type Classes?

With type classes, we can define new behavior for existing types without modifying those types.

This has several advantages:

* Data and behavior remain loosely coupled.
* New behavior can be added without changing existing classes.
* Different implementations can coexist for different types.
* The generic API remains unchanged while behavior varies depending on the type.

This is exactly what **ad-hoc polymorphism** provides: the same operation behaves differently depending on the type of its arguments.

In simple terms:

* **Ad-hoc polymorphism** is a form of polymorphism in which the same operation has different implementations for different types.
* **Type classes** are a mechanism for implementing ad-hoc polymorphism.

#### Further Reading

If you would like to learn more about type classes, the following articles provide excellent introductions:

* [**https://scalac.io/typeclasses-in-scala/**](https://scalac.io/typeclasses-in-scala/)
* [**https://alvinalexander.com/scala/fp-book/type-classes-101-introduction**](https://alvinalexander.com/scala/fp-book/type-classes-101-introduction)
