Scala #
https://apocalisp.wordpress.com/2010/06/08/type-level-programming-in-scala/ https://github.com/milessabin/shapeless http://nerd.kelseyinnis.com/blog/2013/01/07/resources-for-getting-started-with-functional-programming-and-scala/ http://danielwestheide.com/scala/neophytes.html
Typklassen (Type classes) #
Definition #
Eine Typklasse C definiert das Verhalten eines Typs T, indem sie Operationen vorgibt, welche T unterstützen muss. Dabei muss T kein Attribut (member) der Typklasse C sein. Stattdessen kann der Entwickler für einen Typ die Zugehörigkeit zu C festlegen, indem er die Operationen implementiert. Typklassen unterstützen damit Ad-hoc und retroaktiver Polymorphismus.
Implementierung #
Typklassen in Scala erfordern mehrere Schritte.
- Erstens die Typklasse selbst, die in der Regel zustandslos ist, also nur mit den gegebenen Parametern operiert. Bsp.:
object Math { // Define default error message import annotation.implicitNotFound @implicitNotFound("No member of type class NumberLike in scope for ${T}") trait NumberLike[T] { def plus(x: T, y: T): T def divide(x: T, y: Int): T def minus(x: T, y: T): T } }
- Zweitens (nicht zwingend, aber vorteilhaft) eine Reihe von default members:
object Math { // Define default error message import annotation.implicitNotFound trait Number Like[T] { ... } object Number Like { implicit object NumberLikeDouble extends NumberLike[Double] { def plus(x: Double, y: Double); Double = x + y def divide(x: Double, y: Int): Double = x / y def minux(x: Double, y: Double): Double = x - y } implicit object NumberLikeInt extends NumberLike[Int] { def plus(x: Int, y: Int): Int = x + y def divide(x: Int, y: Int): Int = x / y def minus(x: Int, y: Int): Int = x - y } } }
- Drittens die konkrete Anwendung:
object Statistics { import Math.NumberLike def mean[T](xs: Vector[T])(implicit ev: NumberLike[T]): T = ev.divide(xs.reduce(ev.plus(_, _)), xs.size) }
Bei einem impliziten Parameter mit nur einem Typparameter kann dies auch mit einem sog. context bound abgekürzt werden:
scala object Statistics { import Math.NumberLike def mean[T : NumberLike](xs: Vector[T]): T = ev.divide(xs.reduce(ev.plus(_, _)), xs.size) }
Anwendungsfälle #
- Numeric- und Ordering-Typklasse in der Standardbibliothek
- Objekt-Serialisierung und -Deserialisierung in Bibliotheken von Drittanbietern (bspw. JSON): Für eigene Klassen können, wenn sie member einer Formattierungs-Typklasse sind, Wege definiert werden, wie sie in Standardformate (JSON, XML etc.) serialisiert werden
- Mappings zwischen Scala-Typen und Typen, die von einem bestimmten Datenbanktreiber unterstützt werden, werden ebenfalls oftmals mit Typklassen gestalt- und erweiterbar gemacht.