A typeclass example taken from the Programming Scala book:
case class Address(street: String, city: String)
case class Person(name: String, address: Address)
trait ToJSON {
  def toJSON(level: Int = 0): String
  val INDENTATION = "  "
  def indentation(level: Int = 0): (String,String) = 
    (INDENTATION * level, INDENTATION * (level+1))
}
implicit class AddressToJSON(address: Address) extends ToJSON {
  def toJSON(level: Int = 0): String = {
    val (outdent, indent) = indentation(level)
    s"""{
      |${indent}"street": "${address.street}", 
      |${indent}"city":   "${address.city}"
      |$outdent}""".stripMargin
  }
}
implicit class PersonToJSON(person: Person) extends ToJSON {
  def toJSON(level: Int = 0): String = {
    val (outdent, indent) = indentation(level)
    s"""{
      |${indent}"name":    "${person.name}", 
      |${indent}"address": ${person.address.toJSON(level + 1)} 
      |$outdent}""".stripMargin
  }
}
val a = Address("1 Scala Lane", "Anytown")
val p = Person("Buck Trends", a)
println(a.toJSON())
println()
println(p.toJSON())
The code works fine, but I am under the impression (from some blog posts) that typeclasses are typically done this way in scala:
// src/main/scala/progscala2/implicits/toJSON-type-class.sc
case class Address(street: String, city: String)
case class Person(name: String, address: Address)
trait ToJSON[A] {
  def toJSON(a: A, level: Int = 0): String
  val INDENTATION = "  "
  def indentation(level: Int = 0): (String,String) =
    (INDENTATION * level, INDENTATION * (level+1))
}
object ToJSON {
  implicit def addressToJson: ToJSON[Address] = new ToJSON[Address] {
    override def toJSON(address: Address, level: Int = 0) : String = {
          val (outdent, indent) = indentation(level)
          s"""{
              |${indent}"street": "${address.street}",
              |${indent}"city":   "${address.city}"
              |$outdent}""".stripMargin
    }
  }
  implicit def personToJson: ToJSON[Person] = new ToJSON[Person] {
    override def toJSON(a: Person, level: Int): String = {
          val (outdent, indent) = indentation(level)
          s"""{
              |${indent}"name":    "${a.name}",
              |${indent}"address": ${implicitly[ToJSON[Address]].toJSON(a.address, level + 1)}
              |$outdent}""".stripMargin
    }
  }
  def toJSON[A](a: A, level: Int = 0)(implicit ev: ToJSON[A]) = {
    ev.toJSON(a, level)
  }
}
val a = Address("1 Scala Lane", "Anytown")
val p = Person("Buck Trends", a)
import ToJSON.toJSON
println(toJSON(a))
println(toJSON(p))
Which way is better or more correct? Any insights are welcome.
 
    