Effective Programming In Scala — Part 2 : Monads as a way for abstract computations in Scala

Knoldus Inc.
Knoldus - Technical Insights
4 min readJul 13, 2016

Hi Folks,
As we have already gone through the few parts of writting the Scala code in standard way in previous blog https://blog.knoldus.com/2016/06/28/effective-programming-in-scala/. Now carrying forward the concept of effective programming in scala, we are going to discuss about the monads first in this blog.

What is a Monad ?

  • Monad is a structure that represents sequential computations.
  • The type of monad defines the means to chain the various operations together or we can say the nesting of functions of same type. This allows the programmer to build the pipleline, which is used to process the data in a sequence of steps.
  • In the monad the output of a calculation at any step is the input to the other calculation which runs as a parent to the current step. So each action is decorated with additional processing rules provided by the monad.

How is monad defined ?

  • A monad is defined by a type constructor and two operations as bind and return.
  • The operation return requires a value of plain type and puts it into a constructor (which constructs a value), and results in a monadic value M.
  • The operation bind requires its arguments to be a monadic value and a function that can transform the value.
  • Now the bind operation performs some calculation on the plain value (non-monadic value) then returns it to the next function. Then function creates a monadic value, that is fed to the third bind operation composed in the pipeline.

Now we have the following block of code, describing the for-comprehension operation on a list of values,

[code language=”scala”]
for {
item <- (0 to 5).toList
} yield item * 2
[/code]

In the above code the item”*2" operation is performed on elements of a list of numbers. We know that the for-comprehension is a syntactic sugar, compiler reads and transforms the it to a simplified map operation as below,

[code language=”scala”] (0 to 5).toList map (item => item * 2)
[/code]

Now, how can we relate this process with the monads in our case?

We have the following examples of code as below,

Example One

[code language=”scala”]
val evenNumbers = List(2, 4, 6)
val oddNumbers = List(1, 3, 5)
for {
even <- evenNumbers
odd <- oddNumbers
} yield even * odd
[/code]

In the below code we add an extra step to perform the same calculation,

[code language=”scala”]
evenNumbers flatMap { even =>
oddNumbers map { odd =>
even * odd
}
}
[/code]

Example Two

[code language=”scala”]
val evenNumbers = List(2, 4)
val oddNumbers = List(1, 3)
val numberTypes = List(“even”, “odd”)
for {
even <- evenNumbers
odd <- oddNumbers
numberType <- numberTypes
} yield even * odd + numberType
[/code]

And it is converted to the following code,

[code language=”scala”]
evenNumbers flatMap { even =>
oddNumbers flatMap { odd =>
numberTypes map { numberType =>
even * odd + numberType
}
}
}
[/code]

In the above examples, our key and the point of abstraction is flatMap. This is used to binding or chaining of operations in a sequence. In each iteration or step of sequence, the flatMap returns a value of any data structure but not the same. And this value is fed as an input to the for operation to the next step. The flatMap takes an input as a closure and returns a list. One important thing is to notice here is that all the flatMaps take the input of same type and returns also the same type of a closure. Above examples define the List monads of scala. As we may require any condition in the operation at any step, we can put any condition here also.

Now we can define the same structue of operations with the case classes as below,

[code language=”scala”]
case class Name(firstName: String, lastName: String)
case class Person(name: Option[Name], age: Int)
case class Staff(person: Option[Person])

val nameData = Name(“Harsh”, “Sharma”)
val personData = Person(Some(nameData), 25)
val staffData = Some(Staff(Some(personData)))

for {
staff <- staffData
person <- staff.person
name <- person.name
} yield name.firstName
[/code]

Now the above block of for-comprehension returns a chain or sequence of staff, person and the name. If we add an additional step, then it can be converted to the following,

[code language=”scala”]
staffData flatMap { staff =>
staff.person flatMap { person =>
person.name map { name =>
name.firstName
}
}
}
[/code]

flatMap did the magic again

We again used the flatMap to chain the operation running as a sequence. These computations describe that flatMap in each iteration or computation, is types for and with homogeneous values. In the above case, the input type is defined as (A => Option[T]) along with the output as (Option[T]). This kind of monad in the sequential computation is described as MayBe type of monad and Option[T] type of scala.

Conclusion

Common Part

Monads processing is defined as the sequencing or chaining of the operations, which in result, can be used as the abstraction of higher order functions.

Variability Part

In monads processing, the only thing which changed with iteration, is the type which is taken in the computations or operations. In the first step the list is sequenced and in the next step is is changed to Option type.

Magical Part

Functioning of flatMap, which works across the different values of types on the inputs from other operations, and performs the role of a binder or chain manager of the entire process.

So, this was the way to build the monads in scala.

I hope you liked this post, Keep blogging…

KNOLDUS-advt-sticker

--

--

Knoldus Inc.
Knoldus - Technical Insights

Group of smart Engineers with a Product mindset who partner with your business to drive competitive advantage | www.knoldus.com