Slide 1

Slide 1 text

Hi! ! My name is @folone https://github.com/folone/london-scala

Slide 2

Slide 2 text

Type level programming in Scala 1 ! https://github.com/folone/london-scala 1 with lies

Slide 3

Slide 3 text

sealed trait Bool { type &&[B <: Bool] <: Bool type ||[B <: Bool] <: Bool type IfElse[T, F] <: Any } trait True extends Bool { type &&[B <: Bool] = B type ||[B <: Bool] = True type IfElse[T, F] = T } trait False extends Bool { type &&[B <: Bool] = False type ||[B <: Bool] = B type IfElse[T, F] = F }

Slide 4

Slide 4 text

// false || true == true // false.||(true) == true implicitly[False # `||` [True] =:= True] // if(true) String else Int // true.ifElse(string, int) == string implicitly[True # IfElse[String, Int] =:= String] /* if(true) { * if(false) Long else String * } else Int * * true.ifElse(false.ifElse(long, string), string) == string */ implicitly[True # IfElse[False # IfElse[Long, String], Int] =:= String]

Slide 5

Slide 5 text

https://github.com/folone/type-level-birds

Slide 6

Slide 6 text

There's a Smalltalk in your Scala — Stefan Zeiger - Type level Computations in Scala - ScalaIO - 2015

Slide 7

Slide 7 text

Peano numbers!

Slide 8

Slide 8 text

trait Nat trait Z extends Nat trait Succ[A <: Nat] extends Nat type _1 = Succ[Z] type _2 = Succ[_1] type _3 = Succ[_2]

Slide 9

Slide 9 text

trait MinusOne[A <: Nat] { type Res <: Nat } object MinusOne { implicit val baseCase: MinusOne[Z] { type Res = Z } = new MinusOne[Z] { type Res = Z } implicit def inductiveCase[A <: Nat]: MinusOne[Succ[A]] { type Res = A } = new MinusOne[Succ[A]] { type Res = A } }

Slide 10

Slide 10 text

trait MinusOne[A <: Nat] { type Res <: Nat } object MinusOne { type Aux[A <: Nat, Res1 <: Nat] = MinusOne[A] { type Res = Res1 } implicit val baseCase: Aux[Z, Z] = new MinusOne[Z] { type Res = Z } implicit def inductiveCase[A <: Nat]: Aux[Succ[A], A] = new MinusOne[Succ[A]] { type Res = A } }

Slide 11

Slide 11 text

trait Plus[A <: Nat, B <: Nat] { type Res <: Nat } object Plus { implicit def baseCase[A <: Nat]: Plus[A, Z] { type Res = A } = new Plus[A, Z] { type Res = A } implicit def inductiveCase[A <: Nat, B <: Nat, C <: Nat, D <: Nat] (implicit ev0: MinusOne[B] { type Res = C }, ev1: Plus[Succ[A], C] { type Res = D }): Plus[A, B] { type Res = D } = new Plus[A, B] { type Res = D } }

Slide 12

Slide 12 text

implicit def inductiveCase[A <: Nat, B <: Nat, C <: Nat, D <: Nat] (implicit ev0: MinusOne[B] { type Res = C }, ev1: Plus[Succ[A], C] { type Res = D }): Plus[A, B] { type Res = D } = new Plus[A, B] { type Res = D }

Slide 13

Slide 13 text

trait Plus[A <: Nat, B <: Nat] { type Res <: Nat } object Plus { type Aux[A <: Nat, B <: Nat, Res1 <: Nat] = Plus[A, B] { type Res = Res1 } implicit def baseCase[A <: Nat]: Aux[A, Z, A] = new Plus[A, Z] { type Res = A } implicit def inductiveCase[A <: Nat, B <: Nat, C <: Nat, D <: Nat] (implicit ev0: MinusOne.Aux[B, C], ev1: Plus.Aux[Succ[A], C, D]): Aux[A, B, D] = new Plus[A, B] { type Res = D } }

Slide 14

Slide 14 text

type _1 = Succ[Z] type _2 = Succ[_1] type _3 = Succ[_2] @ implicitly[Plus[_1, _2]] res0: Plus[_1, _2] { type Res = _3 } = Plus$$anon$2@f79a760

Slide 15

Slide 15 text

@ implicitly[Plus[_1, _2]] res0: Plus[_1, _2] = Plus$$anon$2@13c3c1e1 @ implicitly[Plus[_1, _2]#Res =:= _3] res1: =:=[Plus[_1, _2]#Res ,_3] =

Slide 16

Slide 16 text

@ implicitly[Plus.Aux[_1, _2, _3]] res0: Plus.Aux[_1, _2, _3] = Plus.Aux$$anon$2@3d08f3f5 @ implicitly[Plus.Aux[_1, _2, Z]] :15: error: could not find implicit value for parameter e: Plus.Aux[_1, _2, Z] implicitly[Plus.Aux[_1, _2, Z]] ^

Slide 17

Slide 17 text

HLists!

Slide 18

Slide 18 text

trait Nat trait Z extends Nat trait Succ[A <: Nat] extends Nat trait HList trait HNil extends HList trait HCons[A, T <: HList] extends HList

Slide 19

Slide 19 text

trait Length[L <: HList] { type Res <: Nat } object Length { implicit val baseCase: Length[HNil.type] { type Res = Z } = new Length[HNil.type] { type Res = Z } implicit def inductiveCase[H, T <: HList, N <: Nat] (implicit ev0: Length[T] { type Res = N }) = new Length[HCons[H, T]] { type Res = Succ[N] } }

Slide 20

Slide 20 text

trait Length[L <: HList] { type Res <: Nat } object Length { type Aux[L <: HList, Res1 <: Nat] = Length[L] { type Res = Res1 } implicit val baseCase: Aux[HNil.type, Z] = new Length[HNil.type] { type Res = Z } implicit def inductiveCase[H, T <: HList, N <: Nat] (implicit ev0: Length.Aux[T, N]) = new Length[HCons[H, T]] { type Res = Succ[N] } }

Slide 21

Slide 21 text

Shapeless! https://github.com/milessabin/shapeless

Slide 22

Slide 22 text

@ import shapeless._ import shapeless._ @ val hlist = 1l :: "hello" :: HNil hlist: Long :: String :: HNil = 1 :: hello :: HNil

Slide 23

Slide 23 text

@ hlist(0) res7: Long = 1 @ hlist(1) res8: String = hello @ hlist(2) :16: error: Implicit not found: Scary[Type].Please#Ignore You requested to access an element at the position TypelevelEncodingFor[2.type] but the HList Long :: String :: HNil is too short. hlist(2) ^ Compilation failed.

Slide 24

Slide 24 text

There's a Prolog in your Scala -- ScalaIO 2015

Slide 25

Slide 25 text

Json serialization

Slide 26

Slide 26 text

def json(o: Any): String

Slide 27

Slide 27 text

def json(o: Any): JsonAst

Slide 28

Slide 28 text

def json[A : Writes](o: A): JsonAst trait Writes[A] { def write(o: A): JsonAst }

Slide 29

Slide 29 text

@ import play.api.libs.json.{Json => PJson} import play.api.libs.json.{Json => PJson} @ case class Thing(id: Long, payload: String) defined class Thing @ PJson.writes[Thing] res0: Writes[Thing] = Writes$$anon$2@f79a760

Slide 30

Slide 30 text

@ case class Omg(_1: Int, _2: Int, _3: Int, _4: Int, _5: Int, _6: Int, _7: Int, _8: Int, _9: Int, _10: Int, _11: Int, _12: Int, _13: Int, _14: Int, _15: Int, _16: Int, _17: Int, _18: Int, _19: Int, _20: Int, _21: Int, _22: Int, _23: Int) defined class Omg @ PJson.writes[Omg] cmd9.sc:1: No unapply or unapplySeq function found for class Omg. val res9 = PJson.writes[Omg] ^ Compilation Failed

Slide 31

Slide 31 text

! >700LOC of macro !

Slide 32

Slide 32 text

Thing(id: Long, payload: String)

Slide 33

Slide 33 text

(Long, String)

Slide 34

Slide 34 text

Long :: String :: HNil

Slide 35

Slide 35 text

1. Case classes are essentially tuples (or HLists) with names. Can we transform one to another? 2. Define Writes for an HList: 1. How do we serialize an HNil? 2. How do we serialize an HCons? 3. !

Slide 36

Slide 36 text

Two (mandatory) building blocks • HLists • Generic2 2 lie: what we actually need is a LabelledGeneric

Slide 37

Slide 37 text

HList @ import shapeless._ import shapeless._ @ val hlist = 1l :: "hello" :: HNil hlist: Long :: String :: HNil = 1 :: hello :: HNil

Slide 38

Slide 38 text

@ hlist(0) res7: Long = 1 @ hlist(1) res8: String = hello @ hlist(2) :16: error: Implicit not found: Scary[Type].Please#Ignore You requested to access an element at the position TypelevelEncodingFor[2.type] but the HList Long :: String :: HNil is too short. hlist(2) ^ Compilation failed.

Slide 39

Slide 39 text

Generic @ case class Thing(id: Long, payload: String) defined class Thing @ val generic = Generic[Thing] generic: shapeless.Generic[Thing]{type Repr = Long :: String :: HNil} = anon$macro$3$1@7f8f5e52

Slide 40

Slide 40 text

@ val representation = generic.to(Thing(1, "hello")) representation: res0.Repr = 1 :: hello :: HNil @ representation(0) res10: Long = 1 @ representation(1) res11: String = hello @ representation(2) :19: error: Implicit not found: Scary[Type].Please#Ignore You requested to access an element at the position TypelevelEncodingFor[2.type] but the HList Long :: String :: HNil is too short. representation(2) ^

Slide 41

Slide 41 text

@ generic.from(hlist) res7: Thing = Thing(1L, "hello")

Slide 42

Slide 42 text

Putting this together 1. Let shapeless transform our case classes into some unified form (HLists) 2. Write down instances of the Writes typeclass for those forms in a generic way (HNil, HCons) 3. Let the magic flow !

Slide 43

Slide 43 text

object writes extends LabelledProductTypeClassCompanion[Writes] with DefaultWrites { object typeClass extends LabelledProductTypeClass[Writes] { override def emptyProduct: Writes[HNil] = Writes(_ => PlayJson.obj()) override def product[H, T <: HList](name: String, headEv: Writes[H], tailEv: Writes[T]) = Writes[H :: T] { case head :: tail => val h = headEv.writes(head) val t = tailEv.writes(tail) PlayJson.obj(name -> h) ++ t } override def project[F, G](instance: => Writes[G], to: F => G, from: G => F) = Writes[F](f => instance.writes(to(f))) } }

Slide 44

Slide 44 text

object writes extends LabelledProductTypeClassCompanion[Writes] with DefaultWrites { object typeClass extends LabelledProductTypeClass[Writes] { override def emptyProduct: Writes[HNil] = Writes(_ => PlayJson.obj()) override def product[H, T <: HList](name: String, headEv: Writes[H], tailEv: Writes[T]) = Writes[H :: T] { case head :: tail => val h = headEv.writes(head) val t = tailEv.writes(tail) PlayJson.obj(name -> h) ++ t } override def project[F, G](instance: => Writes[G], to: F => G, from: G => F) = Writes[F](f => instance.writes(to(f))) } }

Slide 45

Slide 45 text

object writes extends LabelledProductTypeClassCompanion[Writes] with DefaultWrites { object typeClass extends LabelledProductTypeClass[Writes] { override def emptyProduct: Writes[HNil] = Writes(_ => PlayJson.obj()) override def product[H, T <: HList](name: String, headEv: Writes[H], tailEv: Writes[T]) = Writes[H :: T] { case head :: tail => val h = headEv.writes(head) val t = tailEv.writes(tail) PlayJson.obj(name -> h) ++ t } override def project[F, G](instance: => Writes[G], to: F => G, from: G => F) = Writes[F](f => instance.writes(to(f))) } }

Slide 46

Slide 46 text

object writes extends LabelledProductTypeClassCompanion[Writes] with DefaultWrites { object typeClass extends LabelledProductTypeClass[Writes] { override def emptyProduct: Writes[HNil] = Writes(_ => PlayJson.obj()) override def product[H, T <: HList](name: String, headEv: Writes[H], tailEv: Writes[T]) = Writes[H :: T] { case head :: tail => val h = headEv.writes(head) val t = tailEv.writes(tail) PlayJson.obj(name -> h) ++ t } override def project[F, G](instance: => Writes[G], to: F => G, from: G => F) = Writes[F](f => instance.writes(to(f))) } }

Slide 47

Slide 47 text

object writes extends LabelledProductTypeClassCompanion[Writes] with DefaultWrites { object typeClass extends LabelledProductTypeClass[Writes] { override def emptyProduct: Writes[HNil] = Writes(_ => PlayJson.obj()) override def product[H, T <: HList](name: String, headEv: Writes[H], tailEv: Writes[T]) = Writes[H :: T] { case head :: tail => val h = headEv.writes(head) val t = tailEv.writes(tail) PlayJson.obj(name -> h) ++ t } override def project[F, G](instance: => Writes[G], to: F => G, from: G => F) = Writes[F](f => instance.writes(to(f))) } }

Slide 48

Slide 48 text

By @davegurnell

Slide 49

Slide 49 text

-? https://github.com/folone/london-scala @folone