A practical introduction to Category Theory - Voxxed Days Zurich 2017

A practical introduction to Category Theory - Voxxed Days Zurich 2017

Category Theory has become one of the hot topics in our community. Why is this theory suddenly so interesting for developers? Why are the cool kids talking so much about it? How can we apply its principles in our code? This talk will introduce the general principles behind Category Theory, it will show practical examples of how this theory has managed to simplify and solve common challenges that we encounter in our code daily.

E99b07644586e9e1723757bf8e34ea68?s=128

Daniela Sfregola

February 23, 2017
Tweet

Transcript

  1. A PRACTICAL INTRODUCTION TO CATEGORY THEORY @DANIELASFREGOLA VOXXED DAYS ZURICH

    2017
  2. HELLOOOOO > ex Java Developer > OOP background > I

    am not a mathematician !
  3. HOW DO WE REASON ?

  4. COMPOSITION ABSTRACTION

  5. OBJECT ORIENTED PROGRAMMING (OOP) > composition of objects > abstraction

    over the behaviour of each object
  6. FUNCTIONAL PROGRAMMING (FP) > composition of functions > abstraction over

    mathematical laws
  7. CATEGORY THEORY

  8. CATEGORY THEORY ARROW THEORY

  9. WHAT IS A CATEGORY?

  10. WHAT IS A CATEGORY?

  11. MONAD

  12. MONAD TODO LIST PATTERN

  13. LET'S BAKE SOME PIZZA! 1) get the dough 2) get

    the sauce 3) get the cheese 3) cook in the oven until golden
  14. OOP APPROACH Ingredient dough = getDough() Ingredient sauce = getSauce()

    Ingredient cheese = getCheese() def cook(ingredients: List[Ingredient]): Pizza = ??? if (dough != null && sauce != null && cheese != null) cook(dough, sauce, cheese) else EXPLODE
  15. MONADIC APPROACH def getDough(): Option[Ingredient] = ??? def getSauce(): Option[Ingredient]

    = ??? def getCheese(): Option[Ingredient] = ??? def cook(ingredients: List[Ingredient]): Pizza = ??? for { dough <- getDough() sauce <- getSauce() cheese <- getCheese() } yield cook(dough, sauce, cheese)
  16. DIFFERENT TYPES OF MONADS > Option > Try > Future

    > ... > ...Yours here!
  17. DATA VALIDATION > In almost every application > Can be

    come complex quite quickly > Needs to be maintained
  18. CASE STUDY

  19. SCALA 2.12

  20. MAP scala> Some("daniela").map(s => "yo " + s) res0: Option[String]

    = Some(yo daniela) scala> None.map(s => "yo " + s) res1: Option[String] = None
  21. FLATMAP map + flatten scala> Some("daniela").flatMap(s => Some("yo " +

    s)) res2: Option[String] = Some(yo daniela) scala> None.flatMap(s => Some("yo " + s)) res3: Option[String] = None
  22. FOR-COMPREHENSION map + flatMap + (filter) scala > for {

    a <- Some(1); b <- Some(5) } yield a + b res4: Option[Int] = Some(6) scala > for { a <- Some(1); b <- None } yield a + b res5: Option[Int] = None
  23. OPTION package scala sealed abstract class Option[A] final case class

    Some[A](x: A) extends Option[A] case object None extends Option[Nothing] def map[B](f: A => B): Option[B] = ??? def flatMap[B](f: A => Option[B]): Option[B] = ???
  24. OPTION case class Data(email: String, phone: String) def validateEmail(e: String):

    Option[String] = ??? def validatePhone(p: String): Option[String] = ??? def validateData(d: Data): Option[Data] = for { validEmail <- validateEmail(d.email) validPhone <- validatePhone(d.phone) } yield new Data(validEmail, validPhone)
  25. OPTION val okEmail = "email@email.com"; val badEmail = "email.com" val

    okPhone = "+1 1234567890123"; val badPhone = "not-a-valid-phone" > validateData(Data(okEmail, okPhone)) res0: Option[Data] = Some(Data(email@email.com,+1 1234567890123)) > validateData(Data(badEmail, badPhone)) res1: Option[Data] = None > validateData(Data(okEmail, badPhone)) res2: Option[Data] = None > validateData(Data(badEmail, okPhone)) res3: Option[Data] = None
  26. Y U NO TELL ME WHICH ONE IS THE WRONG

    ONE?
  27. EITHER package scala.util sealed abstract class Either[A, B] final case

    class Left[A](a: A) extends Either[A, B] final case class Right[B](b: B) extends Either[A, B] def map[D](f: B => D): Either[A, D] = ??? def flatMap[AA >: A, D](f: B => Either[AA, D]): Either[AA, D] = ???
  28. EITHER case class Data(email: String, phone: String) def validateEmail(e: String):

    Either[List[String], String] = ??? def validatePhone(p: String): Either[List[String], String] = ??? def validateData(d: Data): Either[List[String], Data] = for { validEmail <- validateEmail(d.email) validPhone <- validatePhone(d.phone) } yield new Data(validEmail, validPhone)
  29. EITHER val okEmail = "email@email.com"; val badEmail = "email.com" val

    okPhone = "+1 1234567890123"; val badPhone = "not-a-valid-phone" > validateData(Data(okEmail, okPhone)) res0: Either[List[String],Data] = Right(Data(email@email.com,+1 1234567890123)) > validateData(Data(badEmail, badPhone)) res1: Either[List[String],Data] = Left(List("Invalid email format")) > validateData(Data(okEmail, badPhone)) res2: Either[List[String],Data] = Left(List("Phone number must be numeric")) > validateData(Data(badEmail, okPhone)) res3: Either[List[String],Data] = Left(List("Invalid email format"))
  30. EITHER > only one validation is performed > ideal only

    when error accumulation is not needed
  31. EITHER case class Data(email: String, phone: String) def validateEmail(e: String):

    Either[List[String], String] = ??? def validatePhone(p: String): Either[List[String], String] = ??? def validateData(d: Data): Either[List[String], Data] = { val validEmail = validateEmail(d.email) val validPhone = validatePhone(d.phone) (validEmail, validPhone) match { case (Right(e), Right(p)) => Right(new Data(e, p)) case (Left(errE), Left(errP)) => Left(errE ++ errP) case (Left(errE), _) => Left(errE) case (_, Left(errP)) => Left(errP) } }
  32. EITHER val okEmail = "email@email.com"; val badEmail = "email.com" val

    okPhone = "+1 1234567890123"; val badPhone = "not-a-valid-phone" > validateData(Data(okEmail, okPhone)) res0: Either[List[String],Data] = Right(Data(email@email.com,+1 1234567890123)) > validateData(Data(badEmail, badPhone)) res1: Either[List[String],Data] = Left(List("Invalid email format", "Phone number must be numeric")) > validateData(Data(okEmail, badPhone)) res2: Either[List[String],Data] = Left(List("Phone number must be numeric")) > validateData(Data(badEmail, okPhone)) res3: Either[List[String],Data] = Left(List("Invalid email format"))
  33. EITHER Which one is the error? Which one is the

    valid value? Combine Either instances is not always easy or maintainable
  34. APPLICATIVE FUNCTOR

  35. VALIDATED package cats.data sealed abstract class Validated[+E, +A] final case

    class Valid[+A](a: A) extends Validated[Nothing, A] final case class Invalid[+E](e: E) extends Validated[E, Nothing] def map[B](f: A => B): Validated[E,B] // no flatmap //...but we have something else *really* useful!
  36. VALIDATED AND APPLY* import cats.Apply import cats.data.Validated import cats.implicits._ def

    accumulate[E, A1, A2, B](v1: Validated[E, A1], v2: Validated[E, A2]) (f: (A1, A2) => B): Validated[E, B] = (v1 |@| v2).map(f) // same as: Apply[Validated[E, ?]].map2(v1,v2)(f) * More info on Apply at http://typelevel.org/cats/typeclasses/apply.html
  37. VALIDATED import cats.implicits._ import cats.data.Validated case class Data(email: String, phone:

    String) def validateEmail(e: String): Validated[List[String], String] = ??? def validatePhone(p: String): Validated[List[String], String] = ??? def validateData(d: Data): Validated[List[String], Data] = { val validEmail = validateEmail(d.email) val validPhone = validatePhone(d.phone) (validEmail |@| validPhone).map(Data) }
  38. VALIDATED val okEmail = "email@email.com"; val badEmail = "email.com" val

    okPhone = "+1 1234567890123"; val badPhone = "not-a-valid-phone" > validateData(Data(okEmail, okPhone)) res0: cats.data.Validated[List[String],Data] = Valid(Data(email@email.com,+1 1234567890123)) > validateData(Data(badEmail, badPhone)) res1: cats.data.Validated[List[String],Data] = Invalid(List("Invalid email format", "Phone number must be numeric")) > validateData(Data(okEmail, badPhone)) res2: cats.data.Validated[List[String],Data] = Invalid(List("Phone number must be numeric")) > validateData(Data(badEmail, okPhone)) res3: cats.data.Validated[List[String],Data] = Invalid(List("Invalid email format"))
  39. SUMMARY > Category Theory: how things compose > Monads: good

    for concatenation > Applicative Functors: good for validation
  40. FORGET ABOUT THE DETAILS FOCUS ON HOW THINGS COMPOSE

  41. WANNA KNOW MORE? > Video: Category Theory for the Working

    Hacker by @PhilipWadler > Video: Category Theory by @BartoszMilewski
  42. THANK YOU! > Code on github: github.com/DanielaSfregola/data-validation > Twitter: @DanielaSfregola

    > Blog: danielasfregola.com