Tutorials Hut

Tutorials Hut

Functional Design Principles In Scala: Functors, Monad and Monoids

Scala is a functional programming language and each functional programming language should follow functional design principles. In this article we will briefly see a few key design principles and their examples.

Functors In Scala

Functor is a FP principal maps two different data types(categories). Scala has functor type implementations but actual functor implementations can be found in libraries like cats etc.

Below are some features of Functor-

  – It provides facility to wrap and unwrap things(categories)

  – It provides two functionalities, one to do mapping and another wrapping.

  – It follows identify and associativity rules.

  – It has a map() function and a unit() function  (can be named anything inside)

  – Is all about applying functionalities on data inside a CONTEXT which are allowed by context.

  – It is a type class.

  – it defines how a map will be applied to data.

Syntax:

map :(A=>B) => F[A]=>F[B]

Example of Functor:

object FunctorsExample {
  case class MyContainer[A](things: A) {
	def map[B](fn: (A) => B): MyContainer[B] = { //Custom Functor

  MyContainer(fn(things))
	}
  }
  val doubleThings = (data: Int) =>data*2

  def main(args: Array[String]): Unit = {
	val mythings = MyContainer(100)
	val result = mythings.map(data => doubleThings(data))
	println(result)
  }
} 

Output

MyContainer(200)

Monad In Scala

Monad is FP principal in Scala, it is used to provide mapping between two  types(categories).

Key points to be noted for Monad

         – Monad is a FP principle which is used for mapping between two types/categories.

         – It is used to create structure to hold things inside and facilitate them.

         – It is a type class

         – On top of the functor it has flatMap() and flatten() functions.

Scala List or Maps don’t have any specific monad implementations but they have feature line monad, map, flatMap() and flatten(), so we can say they somewhat follow Monad FP.

Example Of Monad

See in below example Monad has map() like Functor but also has flatten and flatMap() to take out things if they are wrapped twice.

object MonadExamples {
  def main(args: Array[String]): Unit = {
	//Custom Monads
	case class MyContainer[A](things: A) {

  	def map[B](fn: (A) => B): MyContainer[B] = MyContainer(fn(things))

  	def flatten = things

  	def flatMap[B](fn: (A) => MyContainer[B]): MyContainer[B] = fn(things)

	}
 	//Using Monads
	val mythings = MyContainer(100)
	//In this case by using map we can unwrap, transform things, but things of Monad are still in wrap so we use flatten() and flatMap() to take them out.
	val tripleThings = (data: Int) => MyContainer(data * 3)
	//So here we wont get container of container as flatten will flat it
	println(mythings.map(tripleThings).flatten)
	println(mythings.flatMap(tripleThings))
  }
} 

Output

MyContainer(MyContainer(300))
MyContainer(300)
MyContainer(300)

Monoids In Scala

Monoids are another FP design principle to implement your data types of Monoid. Monoid provides a feature to do binary operation on two inputs and also defines identity/empty/zero element method.

Features and important points related to Monoids

  -Monaid is type together with associativity binary (op) which has an identity element(0),

   – Monoid is a small FP data structure.

   – Monoid is a type class.

   – It is used to perform algebraic calculations on individual elements

   – It is a ADT (algebraic data type)

   – It also belongs to category theory  with one single object in category

   – It is used to build complex systems/computations by combining small operations.

   – They allow to perform single binary operation

In below example we can see so many Monoids implemented and used:

Example:

object MonoidsExample {

  //Monoid data structure or type class
  trait Monoid[T] {
	//should have identity rule and identity element
	def empty : T // name can be identity, empty, zero element
	def combine(arg1: T, arg2: T): T // can be add, op, combine etc.
  }

  //Example Int addition monoid
  val indAdditionMonoid = new Monoid[Int] {
	override def empty: Int = 0
	override def combine(arg1: Int, arg2: Int): Int = arg1+arg2
  }

  implicit val indAdditionMonoid1 = new Monoid[Int] {
	override def empty: Int = 0
	override def combine(arg1: Int, arg2: Int): Int = arg1+arg2
  }

  val intMethodMonoid = new Monoid[Int=>Int] {
	override def empty : Int=>Int = (arg:Int) => arg
	override def combine(arg1: Int=>Int, arg2: Int=>Int) = arg1 andThen arg2
  }

  def combineAll(listInt : List[Int]) (monoid: Monoid[Int] ): Int = {

listInt.fold(monoid.empty)((accumalator, data) => monoid.combine(accumalator,data))
  }
  def main(args: Array[String]): Unit = {

println(indAdditionMonoid.empty)
println(indAdditionMonoid.combine(3,4))
println(intMethodMonoid.combine((data:Int) => data*2, (data:Int) => data*3))
println(combineAll(List(1,2,3))(indAdditionMonoid))
  }
}

Output

0
7
scala.Function1$$Lambda$7/[email protected]
6

 There are other design principles as well but above mentioned are the most used ones in any FP language.

 Reference

 Next Article

  • Scala Interview Questions

















  • Leave a Reply

    Your email address will not be published. Required fields are marked *

    Scroll to Top