简体   繁体   中英

Scala Type Classes Understanding Interface Syntax

I'm was reading about cats and I encountered the following code snippet which is about serializing objects to JSON!

It starts with a trait like this:

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

After this, there are some instances of our domain object:

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

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)
        ))
    }
  // etc...
}

So far so good! I can then use this like this:

import JsonWriterInstances._
Json.toJson(Person("Dave", "dave@example.com"))

Later on I come across something called the interface syntax, which uses extension methods to extend existing types with interface methods like below:

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

This then simplifies the call to serializing a Person as:

import JsonWriterInstances._
import JsonSyntax._
Person("Dave", "dave@example.com").toJson

What I don't understand is that how is the Person boxed into JsonWriterOps such that I can directly call the toJson as though toJson was defined in the Person case class itself. I like this magic, but I fail to understand this one last step about the JsonWriterOps. So what is the idea behind this interface syntax and how does this work? Any help?

This is actually a standard Scala feature, since JsonWriterOps is marked implicit and is in scope, the compiler can apply it at compilation-time when needed. Hence scalac will do the following transformations:

Person("Dave", "dave@example.com").toJson
new JsonWriterOps(Person("Dave", "dave@example.com")).toJson
new JsonWriterOps[Person](Person("Dave", "dave@example.com")).toJson

Side note :

It's much more efficient to implicit classes as value classes like this:

implicit class JsonWriterOps[A](value: A) extends AnyVal

This makes the compiler also optimize away the new object construction, if possible, compiling the whole implicit conversion + method call to a simple function call.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM