Upload
dave-cleaver
View
247
Download
1
Embed Size (px)
Citation preview
Flexible Data Representation with Fixpoint Types
Dave CleaverJanuary 24th, 2017
2
Some Simple Datacase class Employee(first: String, last: String, manages: List[Employee] = List.empty)
3
Some Simple Datacase class Employee(first: String, last: String, manages: List[Employee] = List.empty)
Employee("Nina", "McInroe", List( Employee("Bill", "Lumbergh", List( Employee("Peter", "Gibbons"), Employee("Samir", "Nagheenanajar"), Employee("Michael", "Bolton"))), Employee("Dom", "Portwood")))
4
Storing Employeescase class StoredEmployee (first: String, last: String, manages: List[String] = List.empty)
5
Storing Employeescase class StoredEmployee (first: String, last: String, manages: List[String] = List.empty)
StoredEmployee("Nina", "McInroe", List("blumbergh", "dportwood"))StoredEmployee("Bill", "Lumbergh", List("pgibbons", "snagheenanajar", "mbolton"))StoredEmployee("Peter", "Gibbons")StoredEmployee("Samir", "Nagheenanajar") StoredEmployee("Michael", "Bolton") StoredEmployee("Dom", "Portwood")
6
Abstract out the Typecase class EmployeeF[A](first: String, last: String, manages: List[A] = List.empty)
7
Works for the Simple Typetype StoredEmployee = EmployeeF[String]
8
Works for the Simple Typetype StoredEmployee = EmployeeF[String]
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[...]]]
10
Generalized Recursion at the Value Leveldef fix[A](f: (=> A) => A): A = { lazy val a: A = f(a) a }
11
Generalized Recursion at the Value Leveldef 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) } )
12
Fix at the Type Levelcase class Fix[F[_]](unfix: F[Fix[F]])
13
Employee Fixedtype Employee = Fix[EmployeeF]
14
Employee Fixedtype Employee = Fix[EmployeeF]
Fix[EmployeeF](EmployeeF("Nina", "McInroe", List(Fix(EmployeeF("Bill", "Lumbergh", List( Fix(EmployeeF("Peter", "Gibbons")), Fix(EmployeeF("Samir", "Nagheenanajar")), Fix(EmployeeF("Michael", "Bolton"))))), Fix(EmployeeF("Dom", "Portwood"))))
15
Simple JSON Encodingimplicit def encodeEmployee[A: Encoder]: Encoder[EmployeeF[A]] = Encoder.forProduct3("first", "last", "manages")(e =>
(e.first, e.last, e.manages))
16
Encoding Fixdef 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) }
17
Encoding Fixdef 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]]))
18
EmployeeF is a Functorimplicit object EmployeeFunctor extends Functor[EmployeeF] { def map[A, B](fa: EmployeeF[A])(f: A => B): EmployeeF[B] = fa.copy(manages = fa.manages.map(f)) }
19
Programs and Datatype EmployeeTask[A] = Task[EmployeeF[A]]
def fetch(id: String): EmployeeTask[String] = ???
20
Programs and Datatype EmployeeTask[A] = Task[EmployeeF[A]]
def fetch(id: String): EmployeeTask[String] = ???
def loadEmployee(id: String): Fix[EmployeeTask] = Fix[EmployeeTask](fetch(id).map(_.map(loadEmployee)))
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