Flexible Data Representation with Fixpoint Types

Preview:

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

Recommended