Scala FP- Advanced Concepts 1
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 :
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 :-
- We have an API exposed to user toJson . That is expected to convert any type’s instance to Json .
- As you can observe above method is in JsonWriterOps implicit class that takes a very generic type A .
- 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 :)