Se ha denunciado esta presentación.
Se está descargando tu SlideShare. ×

Demystifying Type Class derivation with Shapeless

Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Anuncio
Próximo SlideShare
C sharp part 001
C sharp part 001
Cargando en…3
×

Eche un vistazo a continuación

1 de 51 Anuncio
Anuncio

Más Contenido Relacionado

Presentaciones para usted (20)

Similares a Demystifying Type Class derivation with Shapeless (20)

Anuncio

Más reciente (20)

Demystifying Type Class derivation with Shapeless

  1. 1. Demystifying Type Class Derivation in Shapeless 1 / 22
  2. 2. // Serialize case class? CSV/JSON/XML/... case class User(id: Long, name: String, active: Boolean) ... def toCSV[T](t: T) = ? 2 / 22
  3. 3. Serialize case class? CSV/JSON/XML/... case class User(id: Long, name: String, active: Boolean) ... def toCSV(u: User) = s"${u.id},{u.name},{u.active}" Result: scala> toCSV(User(1L, "test", true)) res1: String = 1,test,true 2 / 22
  4. 4. Serialize case class? CSV/JSON/XML/Whatever Result: scala> toCSV(User(1L, "test", Admin(1L), Address("Wall str", 192), 1.0)) res1: String = 1, test, 1, Wall str, 192, 1.0 case class User(id: Long, name: String, role: Role, addr: Address, score: Double) sealed trait Role case class Address(street: String, house: Int) ... def toCSV(u: User) = s"${u.id}, ${u.name}, ${toCSV(u.role)}, ${toCSV(u.addr)}, ${u def toCSV(r: Role) = r match { case a: Admin => s"${a.id}, "; case c; Client => s"$ def toCSV(r: Address) = s"${a.street}, ${a.house}" 2 / 22
  5. 5. Problem def toCSV[T](t: T): String = ??? 3 / 22
  6. 6. 3 / 22
  7. 7. Agenda Basics: ADTs, Products & Coproducts Type Class pattern Generic Derivation [live-code] Lazy, Aux Debugging 4 / 22
  8. 8. How do we model our domain? 5 / 22
  9. 9. case class / sealed trait case class sealed trait case class User(id: Long, name: String, active: Boolean, role: Role) sealed trait Role case class Admin(id: Long, special: Boolean) extends Role case class Client(id: Long) extends Role How do we model our domain? 5 / 22
  10. 10. case class / sealed trait TupleN / Either TupleN[...] extends Product scala.Either / cats.Xor / scalaz./ type User = (Long, String, Boolean, Role) type Role = Either[Admin, Client] type Admin = (Long, Boolean) type Client = Long How do we model our domain? 5 / 22
  11. 11. case class / sealed trait TupleN / Either TupleN[...] extends Product scala.Either / cats.Xor / scalaz./ type User = (Long, String, Boolean, Role) type Role = Either[Admin, Client] type Admin = (Long, Boolean) type Client = Long How do we model our domain? 5 / 22
  12. 12. case class / sealed trait TupleN / Either HList / Coproduct HList Coproduct type User = Long :: String :: Boolean :: Role :: HNil type Role = Admin :+: Client :+: CNil type Admin = Long :: Boolean :: HNil type Client = Long :: HNil Abstracting over arity val admin: Role = Inl(2L :: true :: HNil) val sandy: User = 1 :: "Sandy" :: true :: admin :: HNil // res5: User = 1 :: Sandy :: true :: Inl(2 :: true :: HNil) 5 / 22
  13. 13. AND types case class Tuple HList OR types sealed trait Either Coproduct ADT * case class User(id: Long, name: String, active: Boolean, role: Role) sealed trait Role case class Admin(id: Long, special: Boolean) extends Role case class Client(id: Long) extends Role * Stands for Algebraic Data Type 6 / 22
  14. 14. HList val hlist = 1 :: "one" :: 1L :: HNil // res0: shapeless.::[ // Int,shapeless.::[ // String,shapeless.::[Long, // shapeless.HNil // ] // ] // ] = 1 :: "one" :: 1L :: HNil hlist.head // Int hlist.tail.head // String hlist.tail.tail.head // Long val s = hlist.select[String] // returns "one". demo.select[List[Int]] // Compilation error. demo does not contain a List[Int] take, head, tail, map, flatMap, zip 7 / 22
  15. 15. HList De nition: sealed trait HList case class ::[H, T <: HList](head : H, tail : T) extends HList // HCons case object HNil extends HList 7 / 22
  16. 16. HList De nition: sealed trait HList case class ::[H, T <: HList](head : H, tail : T) extends HList // HCons case object HNil extends HList List De nition: sealed trait List[+A] case class ::[A](head: A, tail: List[A]) extends List[A] case object Nil extends List[Nothing] 7 / 22
  17. 17. Coproduct Once you feel comfortable with an HList - for Coproduct it is quite similar sealed trait Coproduct sealed trait :+:[+H, +T <: Coproduct] extends Coproduct case class Inl[+H, +T <: Coproduct](head : H) extends :+:[H, T] case class Inr[+H, +T <: Coproduct](tail : T) extends :+:[H, T] sealed trait CNil extends Coproduct Usage: // 'kind-of' Either[Int, String, Boolean] type Co = Int :+: String :+: Boolean :+: CNil val co1 = Inl(42L) // Int :+: String :+: Boolean :+: CNil val co2 = Inr(Inl("forty-two") // Int :+: String :+: Boolean :+: CNil val co3 = Inr(Inr(Inl(true)) // Int :+: String :+: Boolean :+: CNil 8 / 22
  18. 18. shapeless.Generic 8 / 22
  19. 19. Generic import shapeless.Generic case class User(id: Long, name: String, active: Boolean) val generic = Generic[User] // res0: shapeless.Generic[User]{type Repr = shapeless.::[Long, // shapeless.::[String, // shapeless.::[Boolean, // shapeless.HNil // ] // ] //]} 9 / 22
  20. 20. Generic trait Generic[A] { type Repr def to(value: A): Repr def from(value: Repr): A } Usage: scala> val user = User(1L, "josh", true) user: User = User(1,josh,true) scala> generic.to(user) res2: res1.Repr = 1 :: josh :: true :: HNil scala> generic.from(res2) res3: User = User(1,josh,true) 9 / 22
  21. 21. Generic trait Generic[A] { type Repr def to(value: A): Repr def from(value: Repr): A } Usage: scala> val user = User(1L, "josh", true) user: User = User(1,josh,true) scala> generic.to(user) res2: res1.Repr = 1 :: josh :: true :: HNil scala> generic.from(res2) res3: User = User(1,josh,true) 9 / 22
  22. 22. Type Class pattern 9 / 22
  23. 23. Type Class pattern scala standard library - Numeric (Collection's sum, product, min, max) - Ordering (Collection's sorted) - CanBuildFrom (whole collections api) - IsSeqLike - IsTraversableOnce Cats, Scalaz (all of the functional abstractions like Functor, Applicative, Monad, ...) 10 / 22
  24. 24. scala.Ordering /** * Ordering is a trait whose instances each represent * a strategy for sorting instances of a type. * ... */ trait Ordering[T] extends Comparator[T] { /** * Returns an integer whose sign communicates * how x compares to y. */ def compare(x: T, y: T): Int } Type Class pattern 11 / 22
  25. 25. scala.Ordering De nition trait Ordering[T] extends Comparator[T] { def compare(x: T, y: T): Int } Type Class pattern 11 / 22
  26. 26. scala.Ordering De nition Instances trait Ordering[T] extends Comparator[T] { def compare(x: T, y: T): Int } Type Class pattern object Ordering { implicit val intOrd: Ordering[Int] = new Ordering[Int] { def compare(x: Int, y: Int) = lang.Integer.compare(x, y) } implicit val longOrd: Ordering[Long] = new Ordering[Long] def compare(x: Long, y: Long) = lang.Long.compare(x, y) } ... } 11 / 22
  27. 27. scala.Ordering De nition Instances implicit parameter trait Ordering[T] extends Comparator[T] { def compare(x: T, y: T): Int } def sorted[T](implicit ord: Ordering[T]): Repr ..or context bound def sorted[T: Ordering]: Repr Type Class pattern object Ordering { implicit val intOrd: Ordering[Int] = new Ordering[Int] { def compare(x: Int, y: Int) = lang.Integer.compare(x, y) } implicit val longOrd: Ordering[Long] = new Ordering[Long] def compare(x: Long, y: Long) = lang.Long.compare(x, y) } ... } 11 / 22
  28. 28. scala.Ordering De nition Instances implicit parameter Usage: Type Class pattern List(1, 3, 2).sorted // at this point compiler is searching implicit value // in the ?global?, local scope, imported scope, companion o 12 / 22
  29. 29. Type Class pattern https://github.com/mpilquist/simulacrum @typeclass trait CSVSerializer[A] { @op("§") def serialize(a: A): String } 13 / 22
  30. 30. Type Class pattern https://github.com/mpilquist/simulacrum @typeclass trait CSVSerializer[A] { @op("§") def serialize(a: A): String } trait CSVSerializer[A] { def serialize(x: A, y: A): A } object CSVSerializer { def apply[A](implicit instance: CSVSerializer[A]): CSVSerializer[A] = instance trait Ops[A] { def typeClassInstance: CSVSerializer[A] def self: A def §(y: A): A = typeClassInstance.append(self, y) } trait ToCSVSerializerOps { implicit def toCSVSerializerOps[A](target: A)(implicit tc: CSVSerializer[A]): O val self = target val typeClassInstance = tc } } ... 13 / 22
  31. 31. CSV Serializer Type Class de nition trait CSVSerializer[A] { def serialize(a: A): List[String] } 14 / 22
  32. 32. CSV Serializer Type Class de nition trait CSVSerializer[A] { def serialize(a: A): List[String] } lets de ne some helpers.. object CSVSerializer { def apply[A](implicit serializer: CSVSerializer[A]): CSVSerializer[A] = serializer implicit class WithSerialize[A](a: A) { def toCSV(implicit serializer: CSVSerializer[A]) = serializer.serialize(a).mkString(",") } } 14 / 22
  33. 33. deriving serializer [live-code] 14 / 22
  34. 34. Aux Pattern scala> trait Foo { type T } // defined trait Foo scala> def f(foo: Foo, t: foo.T) = ??? <console>:13: error: illegal dependent method type: parameter may only be referenced in a subsequent parameter section def f(foo: Foo, t: foo.T) = ??? ^ scala> 15 / 22
  35. 35. Aux Pattern scala> trait Foo { type T } // defined trait Foo scala> def f(foo: Foo, t: foo.T) = ??? <console>:13: error: illegal dependent method type: parameter may only be referenced in a subsequent parameter section def f(foo: Foo, t: foo.T) = ??? ^ scala> Solution: scala> trait Foo { type T } // defined trait Foo scala> type Aux[T0] = Foo { type T = T0 } // defined type alias Aux scala> def f[T](foo: Aux[T], t: T) = ??? // f: [T](foo: Aux[T], t: T)Nothing 15 / 22
  36. 36. Aux Pattern type Aux[T, Repr0] = Generic[T] { type Repr = Repr0 } 16 / 22
  37. 37. Implicit divergence nested structure case class Account(id: Long, user: User) case class User(id: Long, name: String, active: Boolean) /*1*/ CSVSerializer[Account] /*2*/ CSVSerializer[::[Long, ::[User, HNil]]] /*3*/ CSVSerializer[::[User, HNil]] /*4*/ CSVSerializer[User] /*5*/ CSVSerializer[::[Long, ::[String, ::[Boolean, HNil]]]] // failed // diverging implicit expansion for type xyz.codefastdieyoung // .CSVSerializer[Long :: String :: Boolean :: shapeless.HNil] the compiler sees the same type constructor twice and the complexity of the type parameters is increasing... 17 / 22
  38. 38. Lazy! import shapeless.Lazy implicit def HConsCSVSerializer[H, T <: HList](implicit hSerializer: Lazy[CSVSerializer[H]], tailSerializer: CSVSerializer[T] ): CSVSerializer[H :: T] = { t => s"${hSerializer(t.head)}, ${tailSerializer(t.tail)}" } wrap diverging implicit parameter... it suppresses implicit divergence at compile time it defers evaluation of the implicit parameter at runtime 17 / 22
  39. 39. shapeless 18 / 22
  40. 40. Generic programming def fmap[A](f: A => B): F[B] = ??? 18 / 22
  41. 41. Debugging implicits 18 / 22
  42. 42. Debugging implicits implicitly the reify @showIn x and of course - IDE short-cuts to lookup implicit values 19 / 22
  43. 43. MY NOSE ITCHES 19 / 22
  44. 44. Issues Compilation Times inductive implicits typelevel fork 2.11+ -Yinduction-heuristics runtime overhead ... 20 / 22
  45. 45. Conclusion 20 / 22
  46. 46. Poly ops Lenses TypeClass type class Peano numbers Utils (the, not-compile, Typable) 21 / 22
  47. 47. 21 / 22
  48. 48. 21 / 22
  49. 49. 21 / 22
  50. 50. Thanks @twist522 github.com/thatwist crossroadlabs.xyz vote.scalaua.com 22 / 22
  51. 51. 22 / 22

×