Fixpoint Types offer us the ability to define flexible data types and reuse them within our codebases. In this talk, I’ll walk through Fixpoint types and how they can be applied to storing, retrieving, and presenting data.
6. Abstract out the Type
case class EmployeeF[A](first: String, last: String, manages: List[A] = List.empty)
6
7. Works for the Simple Type
type StoredEmployee = EmployeeF[String]
7
8. Works for the Simple Type
type StoredEmployee = EmployeeF[String]
8
EmployeeF("Nina", "McInroe", List("blumbergh", "dportwood"))
EmployeeF("Bill", "Lumbergh", List("pgibbons", "snagheenanajar", "mbolton"))
EmployeeF("Peter", "Gibbons")
EmployeeF("Samir", "Nagheenanajar")
EmployeeF("Michael", "Bolton")
EmployeeF("Dom", "Portwood")
9. But the Recursive Type…
type Employee = EmployeeF[EmployeeF[EmployeeF[...]]]
9
10. Generalized Recursion at the Value Level
def fix[A](f: (=> A) => A): A = {
lazy val a: A = f(a)
a
}
10
11. Generalized Recursion at the Value Level
def fix[A](f: (=> A) => A): A = {
lazy val a: A = f(a)
a
}
val factorial = fix[Int => Int](factorial => n => {
if(n == 1) 1 else n * factorial.apply(n - 1)
} )
11
12. Fix at the Type Level
case class Fix[F[_]](unfix: F[Fix[F]])
12
17. Encoding Fix
def encodeFix[F[_]](fEncoder: Encoder[Fix[F]] => Encoder[F[Fix[F]]]): Encoder[Fix[F]] =
new Encoder[Fix[F]] {
final def apply(fixF: Fix[F]) = fEncoder(this).apply(fixF.unfix)
}
implicit val fixedEncoder = encodeFix(encodeEmployee(_: Encoder[Fix[EmployeeF]]))
17
18. EmployeeF is a Functor
implicit object EmployeeFunctor extends Functor[EmployeeF] {
def map[A, B](fa: EmployeeF[A])(f: A => B): EmployeeF[B] =
fa.copy(manages = fa.manages.map(f))
}
18
19. Programs and Data
type EmployeeTask[A] = Task[EmployeeF[A]]
def fetch(id: String): EmployeeTask[String] = ???
19
20. Programs and Data
type EmployeeTask[A] = Task[EmployeeF[A]]
def fetch(id: String): EmployeeTask[String] = ???
def loadEmployee(id: String): Fix[EmployeeTask] =
Fix[EmployeeTask](fetch(id).map(_.map(loadEmployee)))
20
21. Flexible Data Representation with Fixpoint Types
• Abstract your data
- Reduce duplicate data types
- While gaining extra power
• Other useful Recursive Types
- case class Free[F[_], A](run: Either[A, F[Free[F, A]])
- case class Cofree[F[_], A](head: A, tail: F[Cofree[F, A]])
• Twitter: @dscleaver
21