TYPE CLASSES
Ad-Hoc Polymorphismus ohne Unterklassen




       Scala User Group Köln/Bonn
                @scalacgn
                    1
POLYMORPHISMUS




       2
POLYMORPHISMUS
• Parametrisiert

  • Generische   Funktionen und Datentypen




                           2
POLYMORPHISMUS
• Parametrisiert

  • Generische   Funktionen und Datentypen

• Ad-hoc

  • Methoden-    und Operator-Overloading

  • Unterklassen     (Subtype Polymorphismus)

  • Type   Classes

                              2
CASE CLASSES SERIALISIEREN
 package model {
   case class Person(firstName: String, lastName: String)
   case class Restaurant(name: String, address: String)
 }




                                                            3
CASE CLASSES SERIALISIEREN
 package model {
   case class Person(firstName: String, lastName: String)
   case class Restaurant(name: String, address: String)
 }



                                                                         Neue
                             JSON                     CSV       XML
                                                                        Funktion
      Person                 toJson                 toCsv       toXml

     Restaurant              toJson                 toCsv       toXml

     Neuer Typ


                                                            3
ÜBERLADEN
1. object JsonSerializer {
2.   def toJson(person: Person): String = """{ name : "%s %s" }""".format(person.firstName, person.lastName)
3.   def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address)
4. }
5.  
6. object CsvSerializer {
7.   def toCSV(person: Person): String = "%s,%s".format(person.firstName, person.lastName)
8.   def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address)
9. }




                                                       4
ÜBERLADEN
1. object JsonSerializer {
2.   def toJson(person: Person): String = """{ name : "%s %s" }""".format(person.firstName, person.lastName)
3.   def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address)
4. }
5.  
6. object CsvSerializer {
7.   def toCSV(person: Person): String = "%s,%s".format(person.firstName, person.lastName)
8.   def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address)
9. }




       +Einfach
       - Kein Zusammenhang außer Methodenname




                                                       4
UNTERKLASSEN
1. abstract class Serializer {
2.   def toJson: String
3.   def toCSV: String
4. }
5.  
6. case class Person(firstName: String, lastName: String) extends Serializer {
7.   override def toJson: String = """{ name : "%s %s" }""".format(firstName, lastName)
8.   override def toCSV: String = "%s,%s".format(firstName, lastName)
9. }
10.  
11. case class Restaurant(name: String, address: String) extends Serializer {
12.   override def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address)
13.   override def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address)
14. }


1. import models._
2. Person("Max", "Mustermann").toJson




                                                        5
UNTERKLASSEN
1. abstract class Serializer {
2.   def toJson: String
3.   def toCSV: String
4. }
5.  
6. case class Person(firstName: String, lastName: String) extends Serializer {
7.   override def toJson: String = """{ name : "%s %s" }""".format(firstName, lastName)
8.   override def toCSV: String = "%s,%s".format(firstName, lastName)
9. }
10.  
11. case class Restaurant(name: String, address: String) extends Serializer {
12.   override def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address)
13.   override def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address)
14. }


1. import models._
2. Person("Max", "Mustermann").toJson




+ Einfach neue Klassen hinzuzufügen
- Modellklassen müssen von Serializer ableiten

                                                        5
TRAITS
1. trait JsonSerializer {
2.   def toJson: String
3. }
4.  
5. trait CsvSerializer {
6.   def toCSV: String
7. }
8.  
9. case class Person(firstName: String, lastName: String) extends JsonSerializer with CsvSerializer {
10.   override def toJson: String = """{ name : "%s %s" }""".format(firstName, lastName)
11.   override def toCSV: String = "%s,%s".format(firstName, lastName)
12. }
13.  
14. case class Restaurant(name: String, address: String)  extends JsonSerializer with CsvSerializer {
15.   override def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address)
16.   override def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address)
17. }




                                                        6
TRAITS
1. trait JsonSerializer {
2.   def toJson: String
3. }
4.  
5. trait CsvSerializer {
6.   def toCSV: String
7. }
8.  
9. case class Person(firstName: String, lastName: String) extends JsonSerializer with CsvSerializer {
10.   override def toJson: String = """{ name : "%s %s" }""".format(firstName, lastName)
11.   override def toCSV: String = "%s,%s".format(firstName, lastName)
12. }
13.  
14. case class Restaurant(name: String, address: String)  extends JsonSerializer with CsvSerializer {
15.   override def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address)
16.   override def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address)
17. }




- Serialisieren hat nichts mit der Logik zu tun,
  Code steht nicht immer zur Verfügung

                                                        6
TYPE CASING
1. object JsonSerializer {
2.   def toJson(x: AnyRef): String = x match {
3.     case person: Person => """{ name : "%s %s" }""".format(person.firstName, person.lastName)
4.     case r: Restaurant => """{ name : "%s"; address: "%s" }""".format(r.name, r.address)
5.   }
6.  
7. object CsvSerializer {
8.   def toCSV(x: AnyRef): String = x match {
9.     case person: Person => "%s,%s".format(person.firstName, person.lastName)
10.     case r: Restaurant => "%s,%s".format(r.name, r.address)
11.   }
12. }




                                                        7
TYPE CASING
1. object JsonSerializer {
2.   def toJson(x: AnyRef): String = x match {
3.     case person: Person => """{ name : "%s %s" }""".format(person.firstName, person.lastName)
4.     case r: Restaurant => """{ name : "%s"; address: "%s" }""".format(r.name, r.address)
5.   }
6.  
7. object CsvSerializer {
8.   def toCSV(x: AnyRef): String = x match {
9.     case person: Person => "%s,%s".format(person.firstName, person.lastName)
10.     case r: Restaurant => "%s,%s".format(r.name, r.address)
11.   }
12. }




+ Einfach neue Funktionen hinzuzufügen
- Viele Eingriffe bei neuem Typ



                                                        7
TYPE CLASSES (1)
1. package serializer
2.  
3. trait CsvSerializable[T] {
4.   def toCsv(t: T): String
5. }
6.  
7. object CsvSerializable {
8.   import models._
9.  
10.   def toCsv[T](t: T)(implicit serializer: CsvSerializable[T]): String =
11.     serializer.toCsv(t)
12.  
13.   implicit object UserSerializable extends CsvSerializable[Person] {
14.     def toCsv(person: User) String =
15.       "%s,%s".format(person.firstName, person.lastName)
16.   }
17. }



1. import serializer._
2. val user = User(name = "Max Musterbro")
3. val csvHelper = implicitly[CsvSerializable[User]]
4. csvHelper.toCsv(user)
5.  
6. // Oder
7. import serializer.CsvSerializable._
8. toCsv(User(name = "Max Musterbro"))

                                                        8
TYPE CLASSES (2)
1. trait JsonSerializable[T] {
2.   def toJson(t: T): String
3. }
4.  
5. object JsonSerializable {
6.   import models._
7.  
8.   implicit object UserSerializable extends JsonSerializable[Person] {
9.     def toJson(person: Person): String =
10.       """{ name : "%s", status: "%s" }""".format(person.firstName, person.lastName)
11.   }
12.  
13.   class UserSerializer(p: Person) {
14.     def toJson: String = UserSerializable.toJson(p)
15.   }
16.  
17.   implicit def UserToSerializer(p: Person) = new UserSerializer(p)


 1. import serializer.JsonSerializable._
 2. User(name = "Max Musterbro").toJson




                                                          9
TYPE CLASSES (3)




        10
TYPE CLASSES (3)
• Definition




                      10
TYPE CLASSES (3)
• Definition

 • Trait   mit Parameter




                           10
TYPE CLASSES (3)
• Definition

 • Trait   mit Parameter

 • Companion     Object mit Default Implementationen




                                10
TYPE CLASSES (3)
• Definition

  • Trait   mit Parameter

  • Companion      Object mit Default Implementationen

• Beispiele   in Scala: scala.math.Ordering und scala.math.Numeric




                                   10
TYPE CLASSES (3)
• Definition

  • Trait   mit Parameter

  • Companion      Object mit Default Implementationen

• Beispiele   in Scala: scala.math.Ordering und scala.math.Numeric

•+   Gut erweiterbar



                                   10
TYPE CLASSES (3)
• Definition

  • Trait   mit Parameter

  • Companion      Object mit Default Implementationen

• Beispiele   in Scala: scala.math.Ordering und scala.math.Numeric

•+   Gut erweiterbar
• - Viel   Code, implicits manchmal undurchsichtig

                                   10
> CODE

• https://twitter.com/scalacgn

• http://xing.to/scala




                                 11

Type Classes in Scala

  • 1.
    TYPE CLASSES Ad-Hoc Polymorphismusohne Unterklassen Scala User Group Köln/Bonn @scalacgn 1
  • 2.
  • 3.
    POLYMORPHISMUS • Parametrisiert • Generische Funktionen und Datentypen 2
  • 4.
    POLYMORPHISMUS • Parametrisiert • Generische Funktionen und Datentypen • Ad-hoc • Methoden- und Operator-Overloading • Unterklassen (Subtype Polymorphismus) • Type Classes 2
  • 5.
    CASE CLASSES SERIALISIEREN package model {   case class Person(firstName: String, lastName: String)   case class Restaurant(name: String, address: String) } 3
  • 6.
    CASE CLASSES SERIALISIEREN package model {   case class Person(firstName: String, lastName: String)   case class Restaurant(name: String, address: String) } Neue JSON CSV XML Funktion Person toJson toCsv toXml Restaurant toJson toCsv toXml Neuer Typ 3
  • 7.
    ÜBERLADEN 1. object JsonSerializer{ 2.   def toJson(person: Person): String = """{ name : "%s %s" }""".format(person.firstName, person.lastName) 3.   def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address) 4. } 5.   6. object CsvSerializer { 7.   def toCSV(person: Person): String = "%s,%s".format(person.firstName, person.lastName) 8.   def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address) 9. } 4
  • 8.
    ÜBERLADEN 1. object JsonSerializer{ 2.   def toJson(person: Person): String = """{ name : "%s %s" }""".format(person.firstName, person.lastName) 3.   def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address) 4. } 5.   6. object CsvSerializer { 7.   def toCSV(person: Person): String = "%s,%s".format(person.firstName, person.lastName) 8.   def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address) 9. } +Einfach - Kein Zusammenhang außer Methodenname 4
  • 9.
    UNTERKLASSEN 1. abstract classSerializer { 2.   def toJson: String 3.   def toCSV: String 4. } 5.   6. case class Person(firstName: String, lastName: String) extends Serializer { 7.   override def toJson: String = """{ name : "%s %s" }""".format(firstName, lastName) 8.   override def toCSV: String = "%s,%s".format(firstName, lastName) 9. } 10.   11. case class Restaurant(name: String, address: String) extends Serializer { 12.   override def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address) 13.   override def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address) 14. } 1. import models._ 2. Person("Max", "Mustermann").toJson 5
  • 10.
    UNTERKLASSEN 1. abstract classSerializer { 2.   def toJson: String 3.   def toCSV: String 4. } 5.   6. case class Person(firstName: String, lastName: String) extends Serializer { 7.   override def toJson: String = """{ name : "%s %s" }""".format(firstName, lastName) 8.   override def toCSV: String = "%s,%s".format(firstName, lastName) 9. } 10.   11. case class Restaurant(name: String, address: String) extends Serializer { 12.   override def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address) 13.   override def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address) 14. } 1. import models._ 2. Person("Max", "Mustermann").toJson + Einfach neue Klassen hinzuzufügen - Modellklassen müssen von Serializer ableiten 5
  • 11.
    TRAITS 1. trait JsonSerializer{ 2.   def toJson: String 3. } 4.   5. trait CsvSerializer { 6.   def toCSV: String 7. } 8.   9. case class Person(firstName: String, lastName: String) extends JsonSerializer with CsvSerializer { 10.   override def toJson: String = """{ name : "%s %s" }""".format(firstName, lastName) 11.   override def toCSV: String = "%s,%s".format(firstName, lastName) 12. } 13.   14. case class Restaurant(name: String, address: String)  extends JsonSerializer with CsvSerializer { 15.   override def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address) 16.   override def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address) 17. } 6
  • 12.
    TRAITS 1. trait JsonSerializer{ 2.   def toJson: String 3. } 4.   5. trait CsvSerializer { 6.   def toCSV: String 7. } 8.   9. case class Person(firstName: String, lastName: String) extends JsonSerializer with CsvSerializer { 10.   override def toJson: String = """{ name : "%s %s" }""".format(firstName, lastName) 11.   override def toCSV: String = "%s,%s".format(firstName, lastName) 12. } 13.   14. case class Restaurant(name: String, address: String)  extends JsonSerializer with CsvSerializer { 15.   override def toJson(r: Restaurant) = """{ name : "%s"; address: "%s" }""".format(r.name, r.address) 16.   override def toCSV(r: Restaurant): String = "%s,%s".format(r.name, r.address) 17. } - Serialisieren hat nichts mit der Logik zu tun, Code steht nicht immer zur Verfügung 6
  • 13.
    TYPE CASING 1. objectJsonSerializer { 2.   def toJson(x: AnyRef): String = x match { 3.     case person: Person => """{ name : "%s %s" }""".format(person.firstName, person.lastName) 4.     case r: Restaurant => """{ name : "%s"; address: "%s" }""".format(r.name, r.address) 5.   } 6.   7. object CsvSerializer { 8.   def toCSV(x: AnyRef): String = x match { 9.     case person: Person => "%s,%s".format(person.firstName, person.lastName) 10.     case r: Restaurant => "%s,%s".format(r.name, r.address) 11.   } 12. } 7
  • 14.
    TYPE CASING 1. objectJsonSerializer { 2.   def toJson(x: AnyRef): String = x match { 3.     case person: Person => """{ name : "%s %s" }""".format(person.firstName, person.lastName) 4.     case r: Restaurant => """{ name : "%s"; address: "%s" }""".format(r.name, r.address) 5.   } 6.   7. object CsvSerializer { 8.   def toCSV(x: AnyRef): String = x match { 9.     case person: Person => "%s,%s".format(person.firstName, person.lastName) 10.     case r: Restaurant => "%s,%s".format(r.name, r.address) 11.   } 12. } + Einfach neue Funktionen hinzuzufügen - Viele Eingriffe bei neuem Typ 7
  • 15.
    TYPE CLASSES (1) 1.package serializer 2.   3. trait CsvSerializable[T] { 4.   def toCsv(t: T): String 5. } 6.   7. object CsvSerializable { 8.   import models._ 9.   10.   def toCsv[T](t: T)(implicit serializer: CsvSerializable[T]): String = 11.     serializer.toCsv(t) 12.   13.   implicit object UserSerializable extends CsvSerializable[Person] { 14.     def toCsv(person: User) String = 15.       "%s,%s".format(person.firstName, person.lastName) 16.   } 17. } 1. import serializer._ 2. val user = User(name = "Max Musterbro") 3. val csvHelper = implicitly[CsvSerializable[User]] 4. csvHelper.toCsv(user) 5.   6. // Oder 7. import serializer.CsvSerializable._ 8. toCsv(User(name = "Max Musterbro")) 8
  • 16.
    TYPE CLASSES (2) 1.trait JsonSerializable[T] { 2.   def toJson(t: T): String 3. } 4.   5. object JsonSerializable { 6.   import models._ 7.   8.   implicit object UserSerializable extends JsonSerializable[Person] { 9.     def toJson(person: Person): String = 10. """{ name : "%s", status: "%s" }""".format(person.firstName, person.lastName) 11.   } 12.   13.   class UserSerializer(p: Person) { 14.     def toJson: String = UserSerializable.toJson(p) 15.   } 16.   17.   implicit def UserToSerializer(p: Person) = new UserSerializer(p) 1. import serializer.JsonSerializable._ 2. User(name = "Max Musterbro").toJson 9
  • 17.
  • 18.
    TYPE CLASSES (3) •Definition 10
  • 19.
    TYPE CLASSES (3) •Definition • Trait mit Parameter 10
  • 20.
    TYPE CLASSES (3) •Definition • Trait mit Parameter • Companion Object mit Default Implementationen 10
  • 21.
    TYPE CLASSES (3) •Definition • Trait mit Parameter • Companion Object mit Default Implementationen • Beispiele in Scala: scala.math.Ordering und scala.math.Numeric 10
  • 22.
    TYPE CLASSES (3) •Definition • Trait mit Parameter • Companion Object mit Default Implementationen • Beispiele in Scala: scala.math.Ordering und scala.math.Numeric •+ Gut erweiterbar 10
  • 23.
    TYPE CLASSES (3) •Definition • Trait mit Parameter • Companion Object mit Default Implementationen • Beispiele in Scala: scala.math.Ordering und scala.math.Numeric •+ Gut erweiterbar • - Viel Code, implicits manchmal undurchsichtig 10
  • 24.