Generating Type Class Instances Automatically

Generating Type Class Instances Automatically

The use of type classes has become pervasive in Scala programming. There are several libraries facilitating them to let developers write highly generic code. Because of the encoding of type classes in Scala, we have the whole power of the language (and now even macros) to derive instances of type classes. With proper library support, it is possible to compose small building blocks into type class instances for your own data types with (almost) no boilerplate.

A1216674d5c9747bcdcc716872439137?s=128

Lars Hupel

July 02, 2013
Tweet

Transcript

  1. 2.

    Agenda 1 Type Classes in Scala 2 Representing data types

    3 Abstracting over Type Classes 2 / 13
  2. 3.

    Type Classes in Scala ▶ Scala offers no built-in support

    for type classes ▶ encoding with traits and implicits ▶ full power of the language available Oliveira, Bruno C. D. S., Moors, Adriaan, and Odersky, Martin Type Classes as Objects and Implicits 3 / 13
  3. 4.

    Implementing instances class Vector2D(val x: Int, val y: Int) class

    Vector3D(val x: Int, val y: Int, val z: Int) ▶ we want: default instances for ... ▶ Semigroup (pointwise addition) ▶ Order (lexicographic order) ▶ and more? ▶ very repetitive to implement by hand ▶ need a mechanism to automate that task 4 / 13
  4. 5.

    Implementing instances class Vector2D(val x: Int, val y: Int) class

    Vector3D(val x: Int, val y: Int, val z: Int) ▶ we want: default instances for ... ▶ Semigroup (pointwise addition) ▶ Order (lexicographic order) ▶ and more? ▶ very repetitive to implement by hand ▶ need a mechanism to automate that task 4 / 13
  5. 6.

    Implementing instances class Vector2D(val x: Int, val y: Int) class

    Vector3D(val x: Int, val y: Int, val z: Int) ▶ Scala already provides some automation ▶ generates: ▶ toString ▶ equals ▶ hashCode ▶ but: baked into the compiler 4 / 13
  6. 7.

    Implementing instances case class Vector2D(x: Int, y: Int) case class

    Vector3D(x: Int, y: Int, z: Int) ▶ Scala already provides some automation ▶ generates: ▶ toString ▶ equals ▶ hashCode ▶ but: baked into the compiler 4 / 13
  7. 8.

    Implementing instances case class Vector2D(x: Int, y: Int) case class

    Vector3D(x: Int, y: Int, z: Int) ▶ Scala already provides some automation ▶ generates: ▶ toString ▶ equals ▶ hashCode ▶ but: baked into the compiler 4 / 13
  8. 9.

    Implementing instances case class Vector2D(x: Int, y: Int) case class

    Vector3D(x: Int, y: Int, z: Int) ▶ Scala already provides some automation ▶ generates: ▶ toString ▶ equals ▶ hashCode ▶ but: baked into the compiler 4 / 13
  9. 10.

    Implementing instances case class Vector2D(x: Int, y: Int) case class

    Vector3D(x: Int, y: Int, z: Int) ▶ Scala already provides some automation ▶ generates: ▶ toString ▶ equals ▶ hashCode ▶ but: baked into the compiler 4 / 13
  10. 11.

    Agenda 1 Type Classes in Scala 2 Representing data types

    3 Abstracting over Type Classes 5 / 13
  11. 12.

    The essence of data types ▶ we are dealing with

    algebraic data types ▶ ... consisting of one or more cases ▶ ... consisting of zero or more fields 6 / 13
  12. 13.

    The essence of data types ▶ we are dealing with

    coproducts ▶ ... consisting of one or more products Magalhães, Jose P., Dijsktra, Atze, Jeuring, Johan, and Löh, Andres A generic deriving mechanism for Haskell Oliveira, Bruno C. D. S., and Gibbons, Jeremy Scala for generic programmers 6 / 13
  13. 14.

    Abstract representation Data type case class Vector3D(x: Int, y: Int,

    z: Int) Representation type Repr = (Int, Int, Int) 7 / 13
  14. 15.

    Abstract representation Data type sealed trait Shape case class Circle(radius:

    Int) extends Shape case class Rectangle(height: Int, width: Int) extends Shape Representation type Repr = Either[Int, (Int, Int)] 7 / 13
  15. 16.

    Agenda 1 Type Classes in Scala 2 Representing data types

    3 Abstracting over Type Classes 8 / 13
  16. 17.

    Composing instances Most type classes C[_] support these operations: product

    C[A] => C[B] => C[(A, B)] project C[B] => (A => B, B => A) => C[A] coproduct C[A] => C[B] => C[Either[A, B]] 9 / 13
  17. 18.

    Composing instances Most type classes C[_] support these operations: product

    C[A] => C[B] => C[(A, B)] project C[B] => (A => B, B => A) => C[A] coproduct C[A] => C[B] => C[Either[A, B]] 9 / 13
  18. 19.

    Instances for data types Input Data type T Type class

    C[_] Output Instance C[T] 10 / 13
  19. 20.

    Instances for data types Algorithm 1. analyze data type, compute

    representation result: type Repr 2. construct conversions results: T => Repr, Repr => T 3. gather base instances result: e.g. C[Int], C[String], ... 4. compose base instances using product and coproduct result: C[Repr] 5. use project to obtain final instance result: C[T] 11 / 13
  20. 21.

    Instances for data types Algorithm 1. analyze data type, compute

    representation result: type Repr 2. construct conversions results: T => Repr, Repr => T 3. gather base instances result: e.g. C[Int], C[String], ... 4. compose base instances using product and coproduct result: C[Repr] 5. use project to obtain final instance result: C[T] 11 / 13
  21. 22.

    Instances for data types Algorithm 1. analyze data type, compute

    representation result: type Repr 2. construct conversions results: T => Repr, Repr => T 3. gather base instances result: e.g. C[Int], C[String], ... 4. compose base instances using product and coproduct result: C[Repr] 5. use project to obtain final instance result: C[T] 11 / 13
  22. 23.

    Instances for data types Algorithm 1. analyze data type, compute

    representation result: type Repr 2. construct conversions results: T => Repr, Repr => T 3. gather base instances result: e.g. C[Int], C[String], ... 4. compose base instances using product and coproduct result: C[Repr] 5. use project to obtain final instance result: C[T] 11 / 13
  23. 24.

    Instances for data types Algorithm 1. analyze data type, compute

    representation result: type Repr 2. construct conversions results: T => Repr, Repr => T 3. gather base instances result: e.g. C[Int], C[String], ... 4. compose base instances using product and coproduct result: C[Repr] 5. use project to obtain final instance result: C[T] 11 / 13
  24. 25.

    Problems ▶ base instances are resolved via regular implicit search

    ▶ What happens for (mutually) recursive data types? ▶ solution: make macro aware of recursion (aka tying the knot) ▶ self recursion: easy, use this instead of implicitly ▶ mutual recursion: require user setup ▶ indirect recursion: ongoing ... 12 / 13
  25. 26.

    Problems ▶ base instances are resolved via regular implicit search

    ▶ What happens for (mutually) recursive data types? ▶ solution: make macro aware of recursion (aka tying the knot) ▶ self recursion: easy, use this instead of implicitly ▶ mutual recursion: require user setup ▶ indirect recursion: ongoing ... 12 / 13