Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

ScalaFP: the Reasons Behind Monads

DZone's Guide to

ScalaFP: the Reasons Behind Monads

Having trouble understanding the monad role in Scala? Click here to learn more about monads and how they work!

· Java Zone ·
Free Resource

Verify, standardize, and correct the Big 4 + more– name, email, phone and global addresses – try our Data Quality APIs now at Melissa Developer Portal!

Monads play an important role in functional programming. However, they mostly confuse 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 of where we have a set(Z). 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.

Our focus for this post will be on monads. The first question that comes to mind is “Why Monads?”

The answer is that when we have side effects in our program, the monads come into the picture.

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.
  • This can include accessing files, read data from the 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-effect operations. Our requirements are that 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 following 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. 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.

How Can we Handle These Situations With Functional Programming?

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

Similar to the example discussed earlier where we interacted with the database and the network layer,  we need to wrap the output of all the methods in the  SomeIO monad of a type like  SomeIO[T] . This will help us 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] = { .... }

How Monad Helps Us With the Above Workflow? What Are the Benefits of Changing the Return Types?

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

  • First, the output of one function is the input of another. More information on this can be found 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 that we will use in our monads.

Now, we have an idea — monad has a structure that 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 shown below:

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 a monad that contains any type and two methods, pure  and flatMap. We will elaborate more on this 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 that “if the class contains a 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 with the different name. For example, 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 that 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 that we saw earlier in this blog. Now, let’s have a look at flatMap.

flatMap

  • As we discussed previously, monads are helping us 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 the 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 the inner container and merges it into the outer container. It, then, returns a container of the specific type like Option[T]or List[T] .

Now, we have a brief idea about the monad structure and its methods like pure map, and flatMap. Let take a look at our earlier examples where monads can help us with method composition.

As we know, we have a  SomeIO  monad. The SomeIO  method means that the structure of the 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 the 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 the HOF function that takes A and returns  SomeIO[B]. Because of that. flatMap  will return  SomeIO[B], as well. This 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
           }
        }
    }
}

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

We can also write the 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 ()

Conclusion

Now, we can compose multiple functions easily with the help of monads. These functions may contain side-effects, but we can compose them easily. Scala contains a lot of inbuilt monads. However, sometimes, as per our requirements, we might require to create our custom monad. Or, we may rely on the Scala Cats and Scalaz libraries to provide us with useful monads for writing compositional code. We will be discussing these functions, along with examples, in our future blogs.

Developers! Quickly and easily gain access to the tools and information you need! Explore, test and combine our data quality APIs at Melissa Developer Portal – home to tools that save time and boost revenue. 

Topics:
java ,tutorial ,scalafp ,monads ,flatmap ,map ,functinal programming

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}