Cats is a simple and concise functional programming library for Scala. It appeals to me for its compactness and simpler class hierarchy.

I have no formal education in category theory. But I am a student of theoretical Mathematics and have studied Abstract Algebra. Though I am not aware of formal category theory terms, I will try converting them to simple set theory.

Category theory is concerned with study of algebraic structures. Algebraic system can be described as a set of objects together with some operations for combining them. Example’s can be vectors, groups, fields etc. Or even Sets.

The aim of this blog post is to explain `cats`

first in a very simple lucid way. And they try putting it in code. I will be using some terms:

**Universe**: For example: Integer-Universe would imply a universe containing integers or set of integers. Think of it as our infinite terrestrial universe which contains milky way which contains solar system and ultimately planets.**Space/Type**: A set of elements corresponding to a property. Space exists in universe. You can think Space as a type in scala.

## Functor

Let `A`

and `B`

be space in some universe. And there exists a function `f` from A to B. Then Functor for `X` universe is a mapping from (mapping of space A in X universe) `X(A)` to (mapping of space `B` in universe `X`) `X(B)`

Now what does this mean in scala world? Say we have a function `f` that tells us how to convert from type A to type B. So `f` does `Int => String`.

Now we want a mechanism to convert elements of another universe, say `Option` universe. First we need a mechanism to convert from space `A` in our universe to `Option(A)` (Space A in Option universe). We will see how it happens later below (Applicative).

So now some how we have mapped A to Option universe. Lets call it `Option(A)` space. We wish to convert from space `Option(A)` to space `Option(B)` (i.e. we need to `Option[Int] => Option[String]`) (Or F[Option] in above image)

Functors are exactly for this purpose with `map` being its flagship method:

trait Functor[F[_]] { self => def map[A, B](f: F[A])(f: A => B): F[B] }

implicit val OptionFunctor = new Functor[Option] { override def map[A, B](fa: Option[A])(f: (A) => B): Option[B] = fa map f } Functor[Option].map(Some(1))(x => x*13) Functor[Option].map(Some(1))(x => x.toString)

Functor[Option] is a mapping mechanism from space `Option(A)` to set `Option(B)` in Option universe. Think of it as:

We know how to convert from a space A to another space B in some universe. In another universe X, Functor of X lets us convert from X(A) to X(B)

So it lets you convert from `Option[Int] => Option[String]` or `Option[String] => Option[List[Int]]` etc. In short any space to any space in `Option` universe

**Lift:**

Lift is my favorite method in Functor. Notice with map, provided you have `Option[A]`, map converts to `Option[B]`. i.e. it consumes `Option[A]` object. But the conversion from `Option[A] => Option[B]` is just another function can be useful if it can be pass around as function. i.e. `F[X]` in above image.

def toStri(n:Int) = n.toString val lift: (Option[Int]) => Option[String] = Functor[Option].lift(toStri) lift(Some(1))

If you think, lift seems something like this:

val lift = Functor[Option].map _ //ignore map types

`lift` seems everything of a `map` but as a pure function. And thats how lift is implemented

def lift[A, B](f: A => B): F[A] => F[B] = x => map(x)(f)

For a function from Space A to Space B, lift provides a function in Universe X to convert from X(A) to X(B)

## Applicative

Now notice, using a Functor we can convert from X(A) to X(B), given a function A to B. But how do we convert an element x belonging to A, to an element in X(A)?

Applicative lets us do that:

implicit val OptionApplicative = new Applicative[Option] { override def pure[A](x: A): Option[A] = Option(x) override def apply[A, B](fa: Option[A])(f: Option[(A) => B]): Option[B] = ... } val op: Option[Int] = Applicative[Option].pure(1)

`pure` lets us convert an element from a universe to another. In above case, it lets us convert from element of type `Int` to `Option[Int]`. Applicative is an `Apply`

## Apply

Apply is also a Functor.

def apply[A, B](fa: F[A])(f: F[A => B]): F[B]

Just instead of the function `f` which converts from `A => B`, you have `X(A => B)`. i.e. a function in X universe. Notice it is not `X[A] => X[B]`.

Lets have some more comprehensive example:

implicit val ListApplicative = new Applicative[List] { override def pure[A](x: A): List[A] = List(x) override def apply[A, B](fa: List[A])(f: List[(A) => B]): List[B] = fa.flatMap(x => f.map(y => y(x))) } val lsOp = Applicative[List].pure(2)

You ask the Applicative for `List` to convert an element 2 to something in `List` universe. i.e. `List(2)`

scala> val ls = Applicative[List].apply(List(1,2,3))(List(x => "a=>"+x, y => "b ==>"+y)) ls: List[String] = List(a=>1, b ==>1, a=>2, b ==>2, a=>3, b ==>3) scala> val ls2 = Applicative[List].apply2(List(1,2,3), List(4,5,6))(List((x:Int,y:Int) => x+" --a--> "+y, (x:Int,y:Int) => x+" --b--> "+y)) ls2: List[String] = List(1 --a--> 4, 1 --b--> 4, 2 --a--> 4, 2 --b--> 4, 3 --a--> 4, 3 --b--> 4, 1 --a--> 5, 1 --b--> 5, 2 --a--> 5, 2 --b--> 5, 3 --a--> 5, 3 --b--> 5, 1 --a--> 6, 1 --b--> 6, 2 --a--> 6, 2 --b--> 6, 3 --a--> 6, 3 --b--> 6)

### Compose:

implicit val OptionApplicative = new Applicative[Option] { override def pure[A](x: A): Option[A] = Option(x) override def apply[A, B](fa: Option[A])(f: Option[(A) => B]): Option[B] = fa.flatMap(x => f.map(y => y(x))) } scala> val comp= (Applicative[List] compose Applicative[Option]) comp: cats.Applicative[[α]List[Option[α]]] = cats.Applicative$$anon$1@168d1858 scala> comp.pure(12) res5: List[Option[Int]] = List(Some(12))

Compose is quite interesting. So say you have an Applicative for List universe and another Applicative for Option universe. By composing them, you get an Applicative for `List[Option]`. Calling pure convert’s 12 from Int to a List(Option(1)).

PS: Will keep updating with time