# Chapter 01: Type classes

## Chapter 1: Introduction

We'll start this chapter by getting familiar with some basic concepts, most of them are abstract Functional Programming concepts that can be seen throughout many languages and tools (especially Haskell) and many are borrowed from `Category Theory`. We'll take a quick look upon `Category Theory` in later chapters.

### TypeClass

A `TypeClass` is a tool that helps us to define a common behavior (feature) for a variety of types. In scala, we usually define a TypeClass as a generic trait :

```scala
trait StringMaker[T] {
  def makeString(t: T): String
}
```

Now we can define an instance of `StringMaker` for any type we want and use the instances wherever we need. In this example, `StringMake` is a **TypeClass**, defined on type `T` and knows how to turn `t`s into strings. In other words, an instance of `StringMaker[X]` adds `makeString` behavior to `x`s. For example, if we want to have an instance of `StringMaker[Int]` we can define it as :

```scala
val intStringMaker = new StringMaker[Int]{ 
  def makeString(t: Int): String = t.toString 
}
```

At this point, you may say to yourself that `StringMaker` and `TypeClasses` are nothing but useless! And you're right since Scala has it's built-in `toString` defined in almost every object. But read on and you'll see the magic.

Next, we'll explore some simple examples of `TypeClass`es to understand the concept better.

#### Equal

Let's create a TypeClass called `Equal` that does one thing only; checking for equality :

```scala
trait Equal[T] {
  def eq(left: T, right: T) : Boolean
}
```

Simple, right? An instance of `Equal[X]` knows how to compare two instances of `X` together and tell whether they are equal.

```
Exercise 1.1: Try to write instances of Equal for a couple of simple types like Int and String and then for a case class like Person
```

Check out [the answer](https://github.com/h-ayat/fp-scalaz/tree/0afc0c90aa331297a7cf28cd9ab25118ad727032/samples/src/main/scala/samples/ch01/Equal.scala) after you finished.

#### Order

Now try to write another TypeClass for object comparison. This one is called `Order`, an instance of `Order` can not only tell us whether two objects are the same, but it can also tell which one is greater or less than others. We'll

```
Exercise 1.2: Define TypeClass Order[T] that has these features : 'gt' to check that left is greater than right, 'eq' to check that left and right are equal, 'gte' to check that left is greater or equal to right. Also define 'lt' and 'lte' alike.
Note: How can we use 'Equal' type class that we've defined before?
Also try to play around with 'Order' and make a few instances for it.
```

Check out [the answer](https://github.com/h-ayat/fp-scalaz/tree/0afc0c90aa331297a7cf28cd9ab25118ad727032/samples/src/main/scala/samples/ch01/Order.scala) after you finished.

#### JsonParser

Now it's easy to design a simple TypeClass for parsing JSON values. For now, we're just interested in the TypeClass itself, not implementations.

```
Exercise 1.3: Design the TypeClass 'JsonParser' that provides two functionalities: 
1. A function that converts an instance of our desired type to it's corresponding json value (output type would be String)
2. A function that does exact opposite of the previous function: form a String to our desired type
```

Check out [the answer](https://github.com/h-ayat/fp-scalaz/tree/0afc0c90aa331297a7cf28cd9ab25118ad727032/samples/src/main/scala/samples/ch01/JsonParser.scala) after you finished.

### TypeClass instances

By now we have a basic understanding of what a TypeClass is. And also we've seen some TypeClass `instance`s. An `instance` of a TypeClass is the implementation of that TypeClass for a specific type. TypeClass only defines behaviors and we have to implement the behavior for all our intended types.

So if we want to use a TypeClass on a given type, we need a TypeClass instance for that type, which means we need to implement lots and lots of instances of various TypeClasses for our types, which may cumbersome. But in reality, it isn't that hard, because: First, Scalaz includes lots of TypeClasses and lots of TypeClass instances for common types. Second: One of the main characteristics of Functional Programming is compositionality, which enables us to mix existing TypeClasses or TypeClass instances and invent new ones.

### TypeClass syntax

So let's get back to using TypeClasses. Imagine that we need to use the `Equal` TypeClass that we've defined in exercise 1.1 to compare people.

```scala
  val p1: Person = ???
  val p2: Person = ???
  personEqual.eq(p1, p2)
```

As you can see, even though using TypeClass instances is easy, it's a little terse, especially if we have to implement `peopleAreEqual` function every single time that we need this type class and for each and every type (like Person). So let's try to generalize this function a little bit so that it would work independently of target Type.

```scala
  def areEqual[T](left: T, right: T)(implicit equal: Equal[T]): Boolean = {
    equal.eq(left, right)
  }
  // Usage:
  val p1: Person = ???
  val p2: Person = ???
  println( areEqual(p1, p2) ) // It's assumed that an implicit instance of Equal[Person] exists in current scope.
```

It's very simple, right? the only thing that `areEqual` does is that it uses an implicit parameter to make working with TypeClasses a little easier(some times this model is called TypeClass interface). Now let us do one final trick:

```scala
class EqualOps[T](t: T)(implicit equal: Equal[T]) {
  def ===(other: T): Boolean = equal.eq(t, other)
}

object EqualSyntax {
  implicit def toEqualOps[T](t: T)(implicit equal: Equal[T]): EqualOps[T] =
    new EqualOps(t)
}
```

So, let's break this code into two parts, EqualOps gets a `t` and an instance of `Equal[T]` (implicitly of course) and enables **dot notation** and a cute little operator to ease our lives. So let's assume that we have a `t: T` and `q: T` that we want to check their equality and we have an implicit instance of `Equal[T]` in context, all we have to do is

```scala
val eq = new EqualOps(t)
eq === q // or eq.===(q)
```

But it's still al little terse and also misleading!! This way we push whoever that reads our code to believe that we are checking equality of `q` against `eq`, not `t` !!! Here `EqualSyntax` comes to the rescue. the method `toEqualsOps` in `EqualSyntax` is an implicit conversion from a `t: T` into it's corresponding `EqualOps[T]` so that we can use equal more easily.

```scala
// import an implicit instance of Equal[T]
import EqualSyntax._

t === q
```

Now that we know how things work to make our life easier, lets write `Ops` and `Syntax` for `Order` TypeClass.

```
Exercise 1.4: write OrderOps that supports these operations: <, >, <=, >=, === and also named operations for compatibility: gt, lt, gte, lte, eq
Next write OrderSyntax.
```

[Checkout the answer here](https://github.com/h-ayat/fp-scalaz/tree/0afc0c90aa331297a7cf28cd9ab25118ad727032/samples/src/main/scala/samples/ch01/OrderSyntax.scala)

### Now, scalaz

Now that we know about TypeClasses, we can move on and start using Scalaz. At the core, Scalaz provides lots of useful TypeClasses and various instances for common types. We can easily import Scalaz and start using it.

```scala
    import scalaz._
    import Scalaz._

    1 =/= 2 // shouldBe true
    1 lte 2 // shouldBe true

    List(1, 2) <= List(3) // shouldBe true

    12.some > none[Int] // shouldBe true
```

[Checkout the sample test here](https://github.com/h-ayat/fp-scalaz/tree/0afc0c90aa331297a7cf28cd9ab25118ad727032/samples/src/test/scala/samples/ch01/ScalazTest.scala)

As you may have guessed, Scalaz provides `Order` and `Equal` TypeClasses (plus Ops and Syntax) and also nice instances for basic types, collections, functions and many other types for us. Note that `12.some` is a special syntactic sugar provided by Scalaz to ease creating an instance of `Option[T]`.

### A taste of contramap

We're now equipped with a technique called TypeClass, two TypeClasses (Equal and Order) and instances of these TypeClasses for lots of basic types (Int, String, etc...). But usually, we use complex data structures rather than these basic types, and obviously, we don't have off-the-shelf TypeClass instances for them.

Let's say we have a **Person** data structure like this:

```scala
case class Person(id: String, name: String, age: String) // Person has lots and lots of other properties too.
```

What if we want an `Order` TypeClass instance for the type Person? The first solution that comes to our mind is obvious: Implement an instance! (Remember that we've already defined Equal\[Person])

```
Exercise 1.5: Implement an instance of order TypeClass for Person that orders people based on their age
```

[Check out the answer here](https://github.com/h-ayat/fp-scalaz/tree/0afc0c90aa331297a7cf28cd9ab25118ad727032/samples/src/main/scala/samples/ch01/Exc5.scala)

Note that here we've implemented the instance as a _Singleton object_ instead of a val.

If you look closely at the various implementations of TypeClass instances, a pattern begins to emerge. When we're implementing an instance for a complex data-structure (like Person), sometimes we can map our complex data structure to a simpler one and then use that in our TypeClass instances. For example, in the above exercise, we see Person as just an Integer that represents a person's age. This means that as long as we have an instance of Order TypeClass for Integer, we don't have to write `Order[Person]` from scratch. This technique is called `Contramap` which we'll deeply investigate in later chapters.

```
Exercise 1.6(Hard): Try to implement Contramap for our two basic TypeClasses:
def contramapEqual[T,U](f: T => U)(implicit Equal[U]): Equal[T]
def contramapOrder[T,U](f: T => U)(implicit Order[U]): Order[T]
```

[Checkout the contramap for equal here](https://github.com/h-ayat/fp-scalaz/tree/0afc0c90aa331297a7cf28cd9ab25118ad727032/samples/src/test/scala/samples/ch01/EqualContramapTest.scala)

[Checkout the contramap for order here](https://github.com/h-ayat/fp-scalaz/tree/0afc0c90aa331297a7cf28cd9ab25118ad727032/samples/src/test/scala/samples/ch01/OrderContramapTest.scala)

So, this is another example of composition, we compose simple small structures to create big ones. In the next chapter we start by first building blocks from the magical `Category Theory`.

## The need for TypeClasses

At this point you might ask yourself "Why bother with TypeClasses?" or "If we want to write equality checker for two type, we'll just write it as a simple function, why writing all this confusing code like TypeClasses or instances or operations?". Honestly, to understand the true power of TypeClasses and `Category Theory`, you need to learn some more stuff, especially some patterns and structures (which we'll see soon enough). But the short answer is that these techniques help us create more powerful abstractions. It helps us define structures that are so generic that can be applied to many problems and specific enough that can deliver real value (because normally, the more abstract a structure gets, the less value it can deliver).

To help you understand better, let's think about `Order` TypeClass, It's true that when you need to compare two objects of type `Person`, you just write something like `def isGreaterThan(a: Person, b: Person): Boolean` and never think about TypeClasses and their pain. But with the TypeClass approach you write an abstract code like this:

```scala
import OrderSyntax._
def isMax[T](l: List[T], t: T)(implicit order: Order[T]): Boolean = l.forall(_ < t)
```

Which checks whether a given object `t` is greater than everything in the given list `l`. It's elegant and beautiful, it's generic and you can use it on any type as long as you have a proper instance of `Order`. Although it's a really simple example (which you can easily replicate it without TypeClasses). This pattern of using TypeClasses with implicit parameters is so widely used that Scala has a special syntax for it, you can write the above code as:

```scala
import OrderSyntax._
def isMax[T: Order](l: List[T], t: T): Boolean = l.forall(_ < t)
```

The part `T: Order` tells the compiler to ensure there is an instance of `Order[T]` available implicitly, so you can omit the implicit parameter.
