Scala FP- Advanced Concepts 1

Sanjeev Ranjan
3 min readJul 4, 2020

--

Folks , hope you are now familiar with scala basics and concepts like type classes and implicits . Now , in this blog we will leverage these basic concepts to take it to the next level . I would now be covering type classes , cats library and for comprehension in depth with their precise application . Before that , i would recommend you to recap what you have already learnt with below links :

  1. Scala FP Blog 1
  2. Scala FP Blog 2
  3. Scala FP Blog 3

Type Classes

So , first advanced feature which you will encounter after implicits in scala is type classes . Type Classes are very powerful concepts in scala , they give you power to add behavior to any library without actually changing its code . Yes , you read it right ! It is sometimes called ad-hoc inheritance or ad-hoc polymorphism. Let me explain it in very simple words . Suppose we have following code in a library (The types below are for purpose of converting something(any type) to Json ) .

sealed trait Json

final case class JsString(value: String) extends Json

final case class JsInteger(value: Int) extends Json

final case class JsDouble(value: Double) extends Json

final case class JsObject(get: Map[String, Json]) extends Json

case object JsNull extends Json

Now we create something called a type class , which is a very generic trait that will work for all types as :

trait JsonWriter[A] {
def write(value: A): Json
}

A is a generic type , so you can use a Person or any other class as well . So, with above two stuffs in library , you can actually write Json Converter for any type . Lets define a class Person and try writing its Converter .

final case class Person(name: String, email: String)

we will have to define instances of type class as below :

object JsonWriterInstances {
implicit val stringWriter: JsonWriter[String] =
new JsonWriter[String] {
def write(value: String): Json =
JsString(value)
}
implicit val personWriter: JsonWriter[Person] =
new JsonWriter[Person] {
def write(value: Person): Json =
JsObject(Map(
"name" -> JsString(value.name),
"email" -> JsString(value.email)
))
}

implicit def optionWriter[A](implicit writer: JsonWriter[A]): JsonWriter[Option[A]] =
new JsonWriter[Option[A]] {
def write(option: Option[A]): Json =
option match {
case Some(aValue) => writer.write(aValue)
case None => JsNull
}
}
}

Now , with above instances in context we can make below example work

object JsonSyntax {

implicit class JsonWriterOps[A](value: A) {
def toJson(implicit w: JsonWriter[A]): Json =
w.write(value)
}
}

object tester2 extends App {

import JsonSyntax._
import JsonWriterInstances._

println(Option(Person("Dave", "sanjeev@example.com")).toJson)
}
O/p:JsObject(Map(name -> JsString(Dave), email -> JsString(sanjeev@example.com)))

Now , let me explain you how the things are working point by point :-

  1. We have an API exposed to user toJson . That is expected to convert any type’s instance to Json .
  2. As you can observe above method is in JsonWriterOps implicit class that takes a very generic type A .
  3. We just had to write the implicit type-class instances (stringWriter ,personWriter , optionWriter )and import it in context to make toJson API work for any type , amazing feature ..isn’t !

I will take a pause here and cover cats library and its type classes in next section . Till then happy learning :)

--

--

Sanjeev Ranjan

Software Engineer || Programming Evangelist || Scala & Java