ScalaFP: Let’s Find The Reasons Behind Monads.

monads
Table of contents
Reading Time: 5 minutes

Monads in Functional Programming play an important role but they mostly confuse the beginners. One of the reasons for the confusion is that they mostly have a knowledge of imperative style programming, where they have no idea about function composition and its importance. In the terms of functional programming, the composition is the root where we have a set(Z) and we compose the elements of the set with the help of functions. For the basic idea of function composition, you can have a walk through my last blog on functors.

Today our focus will be on Monads. So, the first question that comes to our mind “Why Monads?”

The one-liner answer to this question is, when we have side effects in our program, then the monads come into the picture.

Now the next question is “What type of side effects can we have?

  • We are using the database for performing some operations like CRUD. Every operation can return a different result.
  • We are updating the UI as per our requirements, like adding a button to the frame on the basis of some condition but the problem is that the method might return a unit.
  • Accessing files, read data from network and more…

The one truth of application development is: Without side-effects, there is no possibility of building any applications.

Let’s take an example for performing some pure and side-effects operations. Our requirements are like, we need to perform operations like insert, update, find and send the data to the network one after another. So, in the imperative style we need to perform the below steps:

addRecordInDatabase(record: Record): Unit = { .... }

updateUI(): Unit = { .... }

val record = findTheLastRecord(): Record = { .... }

sendRecordToTheNetwork(record: Record): Unit = { .... }

In the above example, every method returns a different value and it is also possible that every method might behave differently in the case of failures. In that case, we need to handle the exceptions according to the method. This makes our code bulky and dirty.

So, “How can we handle these situations in the scenario of functional programming?

As we know, functional programming means to design or write pure functions without any side-effects. But the one bitter truth of functional programming is that there is no way for designing a side-effected function so that it can be pure. We just create an illusion of pure functional using a wrapper or monads and the output of those functions are a monad of a type. We will explore this in later examples.

Like in the example discussed earlier, where we interacted with the database and the network layer, in that case, we need to wrap the output of all the methods in SomeIO monad of some type like SomeIO[T] which will help us to compose the methods easily. So, let’s convert the earlier code into our SomeIO[T] monad.

addRecordInDatabase(record: Record): SomeIO[Unit] = { .... } 

updateUI(): Some[Unit] = { .... } 

val record = findTheLastRecord(): SomeIO[Record] = { .... }

sendRecordToTheNetwork(record: Record):SomeIO[Unit] = { .... }

So, the next questions that pop up in our mind are “How monad help us with above workflow? What are the benefits we get for changing the return types?

First, let’s take the quick brief of the compositions again. There are two types of compositions :

  • First, the output of one function is the input of another, which was mentioned in my last blog.
  • Second, the intermediate result of one function passes to the next inner function initially completing the inner function first and then moving to the outer one which we will use in our Monads.

Now we have an idea, Monad has a structure which contains some methods. But before moving to the structure, we need to remember one thing “Monad is a Functor“.  Let’s take the example and structure of the monad.

trait Monad[F[_]] extends Functor {

    def pure[A](a: A): F[A]

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

}

In the above structure, we have a trait named Monad but the F can be any monad which can contain any type and two methods pure and flatMap, which we will elaborate later but it also extends a Functor trait, meaning that the Monad also contains the map method. In the meantime, the only thing we need to remember for the monads is “If the class contains method map, flatMap and pure, it is called a Monad“.

In our Scala core library, we have existing monads like Option[T], List[T], Future[T] and more. All these monads have the pure method but with the different name. For eg, using the apply method, we convert the normal int value to Option[Int] or List[Int] etc. With Future.successfull(1), we can create Future[Int]. So, this means that every library which contains a map, flatMap and pure(this method name depends on class) can be called a Monad.

Now, we have an idea about the map method from the last blog and the pure method which we saw earlier in this blog. Now let’s have a look at flatMap.

  • As we discussed monads are helping us to pass the intermediate result to our next function if required and perform whatever operation we want to perform. This is only possible because the monads contain flatMap method.
  • flatMap is used, when we have a container within another container like Option[Option[T]] or List[List[T]]. So, it pulls out the elements from inner container and merges it into the outer container and then returns a container of the specific type like Option[T] or List[T].

Now we have a brief idea about monad structure and its methods like pure, map and flatMap. let’s look into our earlier examples where monads can help us with method composition.

As we know, we have a SomeIO monad which means that the structure of monads is something like this:

case class SomeIO[T](value: A) {

    def pure(a: A): SomeIO = { ... }

    def map[A, B](f: A => B): SomeIO[B] = { ... }

   def flatMap[A, B](f: A => SomeIO[B]): SomeIO[B] = { ... }

}

Now, we have our custom monad with all methods required in monads creation. If you want to explore more about method implementations, look at this video. We have an idea, about the map and flatMap method and if we look into the flatMap method, it accepts HOF function which takes A and returns SomeIO[B] and flatMap will return SomeIO[B] as well. That means we can write our example like this:

addRecordInDatabase(record).flatMap { _ => 
    // perform operation after insert record into database
    updateUI().flatMap { _ => 
        // perform operation after update ui
        findTheLastRecord().flatMap { record =>  
           // perform operation after fetching the last record
           sendRecordToTheNetwork(record).map { _ => 
               // perform operation after passes the data to network
           }
        }
    }
}

In this example, we are performing operation one after another as per our requirements with the help of flatMap and map method. With the help of flatMap method, we are achieving this operation because every method returns monad of some type like SomeIO[T] and flatMap is the only method where we can pass HOF with A input and return monad of some type and function itself with the same monad value.

We can also write above solution with the monadic operator ( <- ) or Scala for comprehension which makes our code more concise and readable as below:

for { 
    _ <-  addRecordInDatabase(record) 
    _ <-  updateUI() 
    record  <-  findTheLastRecord() 
    _  <-  sendRecordToTheNetwork(record)
} yield ()

So, in this way, we can compose multiple functions easily with the help of monads which may contain side effects but we can compose them easily. Scala contains a lot of inbuilt monads but sometimes as per our requirements, we might require to create our custom monad or scala-cats and the scalaz libraries provide us with a bunch of most useful monads for writing compositional code. We will be discussing them along with the examples in our future blogs.


knoldus-advt-sticker


 

Written by 

Harmeet Singh is a lead consultant, with experience of more than 5 years. He has expertise in Scala, Java, JVM, and functional programming. On a personal front; he is a food lover.

2 thoughts on “ScalaFP: Let’s Find The Reasons Behind Monads.7 min read

Comments are closed.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading