ScalaFP: Understanding Monoids In Scala Pragmatically

Reading Time: 3 minutes

As we discussed in our previous post, Monoids are semigroups meaning they have properties called closure and associative, along with identity element. So, now our question is, why do we require the identity element? Let’s add this one to our questions which were remaining from our previous post, :

  1. How can we use monoids with Scala?

  2. Where do we require Monoids?

Now let’s answer these questions one by one.

Q. Why do we require the identity element?

Ans: As we have an idea by now that the monoids perform binary operations on the similar type of elements and return the similar type of result. In our semigroups example, we were dealing with case class Money. Let’s suppose, we have a list of elements, i.e money: List[Money]. Now, the company wants to know the total expenses, which they spent in the last financial year. The simple algorithm for that is to add all the money elements available in the list and return the total.

val lastYearExpenses = List(Money(3, 4), Money(34, 5), Money(12, 0))

The requirements here suggest that we will be requiring monoids structure. We require to combine all the money elements one by one and will be returning the total money. So, how can we do that?

Scala collections have a beautiful method or in layman term tools called “fold“, “foldLeft” and “foldRight“. But if we look into the implementation of these methods, they require accumulator for performing these operations. The accumulator is based on the collection element, from where they start to perform the operations. In our case, we have the custom type, Money, that means, we need to define our accumulator based on our money type.

def empty: Money = Money(0, 0)

We know, in monoids, identity element means the element is selected based on the operation and in our case, we need to perform addition(+), so, we have Money(0, 0) as per our identity element requirements. If we look into this code, this helps us to define accumulator for our fold[x] methods.

By using, identity element, we can easily declare an accumulator and perform the operation. The full code example is added below:



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


import cats.kernel.Monoid
object Example1Monoid extends App with Data {
implicit val moneyMonoid = new Monoid[Money] {
override def empty: Money = Money(0, 0)
override def combine(x: Money, y: Money): Money = {
Money(x.dollars + y.dollars + ((x.cents + y.cents) / 100),
(x.cents + y.cents) % 100)
}
}
val lastYearExpenses = List(Money(3, 4), Money(34, 5), Money(12, 0))
def totalAllExpenses(expenses: List[Money])(implicit m: Monoid[Money]): Money = {
expenses.foldLeft(m.empty){
case (acc, money) => m.combine(acc, money)
}
}
println(s"LastYearExpenses : ${totalAllExpenses(lastYearExpenses)}")
}
view raw

Monoid1.scala

hosted with ❤ by GitHub

Now let’s jump to the second question :
Q. How can we use monoids with Scala?

Ans: Using monoids with Scala depends on your requirements. Scala has multiple third-party libraries like Scalaz, Scala-cats, twitter algebird etc . which can be used based on your requirements. You can also create your own abstract layer for monoids and semigroups as well.

Now to answer the last Question :
Q. Where we require Monoids?

Ans: Using this totally depends on your requirements, because sometimes, during designing our domain, we need to identify which type of operations we need to perform.

Like twitter algebird library is used to perform algebraic operations with the help of predefined constructs like monoids, semigroups and more. One of the construct in algebird is Max.
Max is similar to the max function in Int class in scala library.
Let’s try to understand it with the example:

scala> 13 max 2 max 90 max 14 
res0: Int = 90

Max is used for selecting the element with maximum weight from the sequence of elements. In algebird, Max creates monoids for performing this operation internally which you can refer here. Today we will be trying to perform the same thing, but with the help of scala-cats monoids.

Problem Statement :
Suppose we have a popularity contest on Twitter. The user with the most followers wins! Let’s try to figure out the code using Scala Cats :



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


object Example1Monoid extends App {
case class TwitterUser(username: String, followers: Int) extends Ordered[TwitterUser] {
override def compare(that: TwitterUser): Int = {
val c = this.followers that.followers
if(c == 0) this.username.compareTo(that.username) else c
}
}
implicit val twitterUserMonoid = new Monoid[TwitterUser] {
override def empty: TwitterUser = TwitterUser("MinUser", Int.MinValue)
override def combine(x: TwitterUser, y: TwitterUser): TwitterUser = {
if(x.compareTo(y) >= 1) x else y
}
}
case class Max(user: TwitterUser) {
def +(usr: Max)(implicit m: Monoid[TwitterUser]): Max = {
Max(m.combine(this.user, usr.user))
}
}
val harmeetsingh = TwitterUser("singh_harmeet13", 132)
val knoldus = TwitterUser("knolspeak", 575)
val vikas = TwitterUser("vhazrati", 387)
val dzone = TwitterUser("dzone", 10640)
val scala = TwitterUser("scala_lang", 20421)
val winner: Max = Max(harmeetsingh) + Max(knoldus) + Max(vikas) + Max(dzone) + Max(scala)
println(s"Winner Is: $winner")
assert(winner.user == scala)
}
view raw

Monoid2.scala

hosted with ❤ by GitHub

Now that we are through with these examples, I hope you have some decent idea of using monoids pragmatically. Please feel free to comments and give suggestions.

References:


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.

4 thoughts on “ScalaFP: Understanding Monoids In Scala Pragmatically3 min read

Comments are closed.