# Scala Cats library for dummies- part 4

This article will center on cats library **monad** type class, but before we proceed, it will be nice to have a good understanding of the `flatMap`

function in the standard scala collections. `FlatMap`

is basically two functions in one i.e `map`

and `flatten`

** . **As an example, say we want to multiply the elements of two(2) Lists of integers and return a single list, one approach is to use the map function of the first List to iterate over the second List then combine(flatten) the final result.

scala> val first = List(2, 5)

first: List[Int] = List(2, 5)

scala> val second = List(4, 8)

second: List[Int] = List(4, 8)

scala> first.map( e1 => second.map(e2 => e1 * e2))

res0:List[List[Int]]= List(List(8, 16), List(20, 40))

scala> res0.flatten

res1: List[Int] = List(8, 16, 20, 40)

Notice how we used flatten to merge `List[List[Int]]`

into `List[Int]. `

Flatten can also be used for nested `option`

types. lets see a very trivial example.

scala> val a = Option(5)

a: Option[Int] = Some(5)

scala> def intToSome(x:Int) = Some(x)

intToSome: (x: Int)Some[Int]

scala> a.map( o => intToSome(o))

res5:Option[Some[Int]]= Some(Some(5))

scala> res5.flatten

res6: Option[Int] = Some(5)

scala> val a = Option(5)

a: Option[Int] = Some(5)

scala> def intToSome(x:Int) = Some(x)

intToSome: (x: Int)Some[Int]

scala> a.flatMap( x => intToSome(x))

res0: Option[Int] = Some(5)

scala> first.flatMap( f => second.map(s => f * s))

res2: List[Int] = List(8, 16, 20, 40)

In both examples `flatten`

function was used to merge the final result into a single data type. The flatten function takes a nested context **F[F[A]]** to return** F[A]**. We can however use the `flatMap`

function in the standard library to achieve the same computation when ever we find ourselves writing `map`

and `flatten`

expressions.

F[F[A]] => F[A]

Option[Option[Int]] => Option[Int]

List[List[Int]] => List[Int]

Below is the signature of the `flatMap`

function

def flatMap[B](f: A => F[B]): F[B]

Remember, we discussed Applicative `pure`

function back then in part 3 , now, adding `flatMap`

to the Applicative type class gives you a Monad( any instance that have the `pure`

and `flatMap`

* *methods).

We have already seen `flatMap`

in action in our previous List and Option examples above, however, monads are in almost everywhere in all scala collections and data types(Set, Vector, Array, Map , Future* etc ), even though there is no concrete class or trait called monad in the standard scala language.

### Using Monads

Lets have a look at an example of monad usage : say we have a student school management application and we want to implement a module that will compute the individual performance of a student for a particular session. First, we need to retrieve the student profile information, then retrieve all the courses he/she registered for that session, and finally retrieve all the grades of these courses.

case class Grade(id:Int, studentId:Int, grade: String)

case class Student(id: Int, name: String)

case class StudentCourse(id:Int,

studentId: Int,

courseId: Int)

object StudentService{

def getStudentById(id: Int): Option[Student] = {}

}

object StudentCoursesService{

def getCourses(student: Student): Option[List[StudentCourse]]={}

}

object StudentGrades{

def getGrades(lc: List[StudentCourse]): Option[List[Grade]]={}

}

Looking carefully at the return types of the service methods(options), we can simply flatmap everything.

val studentGrade = StudentService.getStudentById(id)

.flatMap(getCourses)

.flatMap(getGrades)

Alternatively, we can use the for-comprehension which is a composition of map and flatMap.

val studentGrade = for {

student <- getStudentById(id)

courses <- getCourses(student)

grades <- getGrades(courses)

}yield grades

if we had to do this without using monads we will having a lot of if/then/else statement or nested loops flying everywhere (*where fixing a bug could possibly give you two free bugs *:{ ).

Libraries that are fully non-blocking with asynchronous I/O operation usually encapsulated their return types in types like `future`

s , one example of such libraries is reactivemongo. When using such libraries you will likely have to use the`future`

‘s flatmap method in composing multiple operations together.

**Monads in Cats**

Essentially,`flatMap`

is the most fundamental function of Monad and authors of cats library also built its monad based on this principle — by using `flatMap`

and `pure`

to implement `flatten`

and `map`

, however, they also added another requirement to all cats library monad implementation, called tailRecM ( Tail recursive Monad), the reason behind this additional requirement is to make monadic recursion stack safe( since monadic recursion are quite common and a poor implementation could lead to stack overflow on the jvm ).

Below is a `Monad`

implementation for `Option`

.

`import scala.annotation.tailrec`

implicit val optionMonad = new Monad[Option] {

def flatMap[A, B](fa: Option[A])(f: A => Option[B]): Option[B] = fa.flatMap(f)

def pure[A](a: A): Option[A] = Some(a)

@tailrec

def tailRecM[A, B](a: A)(f: A => Option[Either[A, B]]): Option[B] = f(a) match {

case None => None

case Some(Left(nextA)) => tailRecM(nextA)(f) // continue the recursion

case Some(Right(b)) => Some(b) // recursion done

}

}

In conclusion, I deliberately did not discuss that monad laws as what we have learnt is enough for you kick start your journey to the land of monads! However, Monads is not everything, but having it in your tool box is definitely a thing to work in the functional world of scala.

Lets take a break from here, but don't hesitate to buzz me if you don't understand any aspect of this writeup. Our next discuss will be on the **OptionT **which is also a monad.

*If you like this writeup, click the💚 below so more people can see it here on Medium.*

Big thanks to @edmundnoble and @TheAlmikey_twitter for reviewing this article.