introduction to type classes in scala
Post on 29-Jan-2018
928 Views
Preview:
TRANSCRIPT
type classes in ScalaAnd how Json Reads/Writes/Formats
work in play framework?
Yann Simon - @simon_yann
toString, hashCode on modelcase 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.toStringtoString()List
Person
String Int
toString()
toString() toString()
persons.hashCodehashCodeList
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 collisionA 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! frameworkpackage 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! frameworkpackage 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 syntaxdef 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.toJsonimplicit class JsonOps[A](a: A)(implicit ser: ToJson[A]) { def toJson: String = ser.serialize(a)} "hello".toJson3.toJsonPerson("bob", 34).toJsonpersons.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
top related