type classes in Scala
And how Json Reads/Writes/Formats
work in play framework?
Yann Simon - @simon_yann
toString, hashCode on model
case class Person(name: String, age: Int)



val persons =

List(

Person("bob", 34),

Person("alice", 34))

persons.toString()

res0: String = List(Person(bob,34), Person(alice,34))
persons.hashCode()
res1: Int = -408691241
persons.toString
toString()List
Person
String Int
toString()
toString() toString()
persons.hashCode
hashCodeList
Person
String Int
hashCode
hashCode hashCode
What about persons.toJson?
persons.toXML? .toCSV?
persons.otherHashCode?
persons.otherToString?
Why persons.toString?
persons.hashCode?
• part of the standard JVM library
package java.lang;



public class Object {

public native int hashCode();

public boolean equals(Object obj) { … }

public String toString() { … }
public final native void notify();

public final native void notifyAll();

public final native void wait(long timeout) throws InterruptedException;

public final void wait(long timeout, int nanos) throws InterruptedException { … }

public final void wait() throws InterruptedException { … }
}
Why persons.toString?
persons.hashCode?
• needed by Map, Set, serialization…
• mix technical concerns with domain model
• no liberty for further improvement
• ex: #2011-003 multiple implementations denial-of-
service via hash algorithm collision

A variety of programming languages suffer from a denial-of-service
(DoS) condition against storage functions of key/value pairs in hash
data structures, the condition can be leveraged by exploiting
predictable collisions in the underlying hashing algorithms.
a good toJson?
• Separated from domain model
• Flexible
• User can override library choices
persons to json
List
Person
String Int
listToJson
personToJson
stringToJson intToJson
trait toJson[A]
ToJson[A] strategy is a
parameter of toJson[A]
def toJson[A](a: A)(serializer: ToJson[A]): String =

serializer.serialize(a)



toJson("hello")(stringToJson)

toJson(3)(intToJson)

toJson(Person("bob", 34))(personToJson)

toJson(persons)(listToJson(personToJson))
find the right ToJson[A]
based on type
def toJson[A](a: A)(implicit serializer: ToJson[A]): String =

serializer.serialize(a)



toJson("hello")

toJson(3)

toJson(Person("bob", 34))

toJson(persons)
find the right ToJson[A]
based on type
toJson("hello")
// is different from
toJson(3)

$ scalac -Xprint:4 Test.scala
[…]
Test.this.toJson[String]("hello")(Test.this.stringToJson);
Test.this.toJson[Int](3)(Test.this.intToJson);
Type classes
• Define a contract (ex in ToJson[A]: (a: A) -> String)
• Define strategy for a tree of classes (like the domain model)
• Use type for find the suitable implementation
• Each implicit can be overridden / set explicitly => the user has the
power to override defaults
• Type classes resolved at compilation time
• Be sure that the strategy is defined for each class
• No runtime introspection necessary
In Play! framework
package play.api.libs.json

object Json {

def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue = tjs.writes(o)

def fromJson[T](json: JsValue)(implicit fjs: Reads[T]): JsResult[T] =
fjs.reads(json)
}
/**

* Default Serializers.

*/

trait DefaultWrites {

implicit object IntWrites extends Writes[Int] {

def writes(o: Int) = JsNumber(o)

}

implicit object StringWrites extends Writes[String] {

def writes(o: String) = JsString(o)

}

implicit def traversableWrites[A: Writes] = new Writes[Traversable[A]] {

def writes(as: Traversable[A]) = JsArray(as.map(toJson(_)).toSeq)

}

}
In Play! framework
package play.api.libs.json

trait Format[A] extends Writes[A] with Reads[A]

/**

* Default Json formatters.

*/

trait DefaultFormat {



implicit def GenericFormat[T](implicit fjs: Reads[T], tjs: Writes[T]):
Format[T] = {

new Format[T] {

def reads(json: JsValue) = fjs.reads(json)

def writes(o: T) = tjs.writes(o)

}

}



}
implicitly syntax
def toJson[A](a: A)(implicit toJson: ToJson[A]): String =

toJson.serialize(a)
// can be written as:

def toJson[A : ToJson](a: A): String =

implicitly[ToJson[A]].serialize(a)


toJson("hello")

toJson(3)

toJson(Person("bob", 34))

toJson(persons)
persons.toJson
implicit class JsonOps[A](a: A)(implicit ser: ToJson[A]) {

def toJson: String = ser.serialize(a)

}



"hello".toJson

3.toJson

Person("bob", 34).toJson

persons.toJson
Questions?
• code on https://github.com/yanns/scala-type-classes-
demo
• more info:
• https://non.github.io/cats/typeclasses.html
• http://danielwestheide.com/blog/2013/02/06/the-
neophytes-guide-to-scala-part-12-type-classes.html

Introduction to type classes in Scala

  • 1.
    type classes inScala And how Json Reads/Writes/Formats work in play framework? Yann Simon - @simon_yann
  • 2.
    toString, hashCode onmodel case class Person(name: String, age: Int)
 
 val persons =
 List(
 Person("bob", 34),
 Person("alice", 34))
 persons.toString()
 res0: String = List(Person(bob,34), Person(alice,34)) persons.hashCode() res1: Int = -408691241
  • 3.
  • 4.
  • 5.
    What about persons.toJson? persons.toXML?.toCSV? persons.otherHashCode? persons.otherToString?
  • 6.
    Why persons.toString? persons.hashCode? • partof the standard JVM library package java.lang;
 
 public class Object {
 public native int hashCode();
 public boolean equals(Object obj) { … }
 public String toString() { … } public final native void notify();
 public final native void notifyAll();
 public final native void wait(long timeout) throws InterruptedException;
 public final void wait(long timeout, int nanos) throws InterruptedException { … }
 public final void wait() throws InterruptedException { … } }
  • 7.
    Why persons.toString? persons.hashCode? • neededby Map, Set, serialization… • mix technical concerns with domain model • no liberty for further improvement • ex: #2011-003 multiple implementations denial-of- service via hash algorithm collision
 A variety of programming languages suffer from a denial-of-service (DoS) condition against storage functions of key/value pairs in hash data structures, the condition can be leveraged by exploiting predictable collisions in the underlying hashing algorithms.
  • 8.
    a good toJson? •Separated from domain model • Flexible • User can override library choices
  • 9.
    persons to json List Person StringInt listToJson personToJson stringToJson intToJson trait toJson[A]
  • 10.
    ToJson[A] strategy isa parameter of toJson[A] def toJson[A](a: A)(serializer: ToJson[A]): String =
 serializer.serialize(a)
 
 toJson("hello")(stringToJson)
 toJson(3)(intToJson)
 toJson(Person("bob", 34))(personToJson)
 toJson(persons)(listToJson(personToJson))
  • 11.
    find the rightToJson[A] based on type def toJson[A](a: A)(implicit serializer: ToJson[A]): String =
 serializer.serialize(a)
 
 toJson("hello")
 toJson(3)
 toJson(Person("bob", 34))
 toJson(persons)
  • 12.
    find the rightToJson[A] based on type toJson("hello") // is different from toJson(3)
 $ scalac -Xprint:4 Test.scala […] Test.this.toJson[String]("hello")(Test.this.stringToJson); Test.this.toJson[Int](3)(Test.this.intToJson);
  • 13.
    Type classes • Definea contract (ex in ToJson[A]: (a: A) -> String) • Define strategy for a tree of classes (like the domain model) • Use type for find the suitable implementation • Each implicit can be overridden / set explicitly => the user has the power to override defaults • Type classes resolved at compilation time • Be sure that the strategy is defined for each class • No runtime introspection necessary
  • 14.
    In Play! framework packageplay.api.libs.json
 object Json {
 def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue = tjs.writes(o)
 def fromJson[T](json: JsValue)(implicit fjs: Reads[T]): JsResult[T] = fjs.reads(json) } /**
 * Default Serializers.
 */
 trait DefaultWrites {
 implicit object IntWrites extends Writes[Int] {
 def writes(o: Int) = JsNumber(o)
 }
 implicit object StringWrites extends Writes[String] {
 def writes(o: String) = JsString(o)
 }
 implicit def traversableWrites[A: Writes] = new Writes[Traversable[A]] {
 def writes(as: Traversable[A]) = JsArray(as.map(toJson(_)).toSeq)
 }
 }
  • 15.
    In Play! framework packageplay.api.libs.json
 trait Format[A] extends Writes[A] with Reads[A]
 /**
 * Default Json formatters.
 */
 trait DefaultFormat {
 
 implicit def GenericFormat[T](implicit fjs: Reads[T], tjs: Writes[T]): Format[T] = {
 new Format[T] {
 def reads(json: JsValue) = fjs.reads(json)
 def writes(o: T) = tjs.writes(o)
 }
 }
 
 }
  • 16.
    implicitly syntax def toJson[A](a:A)(implicit toJson: ToJson[A]): String =
 toJson.serialize(a) // can be written as:
 def toJson[A : ToJson](a: A): String =
 implicitly[ToJson[A]].serialize(a) 
 toJson("hello")
 toJson(3)
 toJson(Person("bob", 34))
 toJson(persons)
  • 17.
    persons.toJson implicit class JsonOps[A](a:A)(implicit ser: ToJson[A]) {
 def toJson: String = ser.serialize(a)
 }
 
 "hello".toJson
 3.toJson
 Person("bob", 34).toJson
 persons.toJson
  • 18.
    Questions? • code onhttps://github.com/yanns/scala-type-classes- demo • more info: • https://non.github.io/cats/typeclasses.html • http://danielwestheide.com/blog/2013/02/06/the- neophytes-guide-to-scala-part-12-type-classes.html