Scala IO 2017 - A Pragmatic Introduction to Category Theory

Scala IO 2017 - A Pragmatic Introduction to Category Theory

Category Theory has become one of the hot topics in the community. Why is this theory suddenly so interesting for developers? Why are the cool kids talking so much about it? This talk will introduce the general principles of Category Theory in a pragmatic, non-mathematical way. We will show practical examples of how this theory has managed to simplify and solve common challenges that we encounter in our code daily, such as nullable values, error handling, parallel and sequential operations and data validation. Also, we will apply them to create our own category theory library from scratch using Scala and ScalaCheck as the only dependency.

E99b07644586e9e1723757bf8e34ea68?s=128

Daniela Sfregola

November 02, 2017
Tweet

Transcript

  1. A PRAGMATIC INTRODUCTION TO CATEGORY THEORY @DANIELASFREGOLA SCALA IO 2017

    github.com/DanielaSfregola/tutorial-cat
  2. Every good idea is discovered twice: once by a mathematician

    once by a computer scientist — Philip Wadler
  3. A Pragmatic Intro to Category Theory by Daniela Sfregola THURSDAY

    10.00 AM vs A crash course on Category Theory by Bartosz Milewski FRIDAY 15.30 PM
  4. HELLOOOOO > ex Java Developer > OOP background > I

    am not a mathematician !
  5. I AM NOT A MATHEMATICIAN

  6. YOU DO NOT NEED TO KNOW CATEGORY THEORY TO WRITE

    SCALA CODE
  7. YOU DO NOT NEED TO KNOW CATEGORY THEORY TO WRITE

    FUNCTIONAL CODE
  8. CATEGORY THEORY DEEPER UNDERSTANDING ON OUR CODE

  9. HOW DO WE REASON ?

  10. COMPOSITION ABSTRACTION

  11. CATEGORY THEORY HOW THINGS COMPOSE

  12. ARROW THEORY CATEGORY THEORY HOW THINGS COMPOSE

  13. WHAT IS A CATEGORY?

  14. COMPOSITION LAW

  15. IDENTITY LAW

  16. COMPOSITION + ASSOCIATIVITY

  17. CATEGORY'S RULES > Identity > Composition > Associativity

  18. A PRACTICAL EXAMPLE

  19. CATEGORY WITH 1 OBJECT

  20. CATEGORY WITH 1 OBJECT = MONOID

  21. MONOID'S RULES Identity n o id == id o n

    == n Composition forall x, y => x o y Associativity x o (y o z) == (x o y) o z
  22. SCALACHECK FTW! MonoidSpec // n o id == id o

    n == n property("identity") = forAll { n: A => monoid.compose(n, id) == n && monoid.compose(id, n) == n } // forall x, y => x o y property("composition") = forAll { (x: A, y: A) => monoid.compose(x, y).isInstanceOf[A] } // x o (y o z) == (x o y) o z property("associativity") = forAll { (x: A, y: A, z: A) => val xY = monoid.compose(x,y) val yZ = monoid.compose(y,z) monoid.compose(xY, z) == monoid.compose(x, yZ) }
  23. A PRACTICAL EXAMPLE

  24. MONOID trait Monoid[A] { def identity: A def compose(x: A,

    y: A): A }
  25. MONOID INSTANCES (1) implicit val intMonoid: Monoid[Int] = new Monoid[Int]

    { def compose(x: Int, y: Int): Int = x + y def identity: Int = 0 }
  26. MONOID INSTANCES (2) implicit val stringMonoid: Monoid[String] = new Monoid[String]

    { def compose(x: String, y: String): String = x + y def identity: String = "" }
  27. CATEGORY WITH 1+ OBJECT

  28. CATEGORY IN A BOX

  29. CATEGORY IN A BOX > Objects are in a Box

    > All the arrows are copied
  30. LIFTING: CONTEXT VS CONTENT

  31. EXAMPLE OF BOXES > Option > Future > Try >

    List > Either
  32. CATEGORY IN A BOX = FUNCTOR

  33. FUNCTOR'S RULES Identity map(id) == id Composition map(g o f)

    == map(g) o map(f) Associativity map(h o g) o map(f) == map(h) o map(g o f)
  34. SCALACHECK FTW! FunctorSpec // map_id == id property("identity") = forAll

    { box: Box[A] => map(box)(identity) == box } // map_(g o f) == (map_g) o (map_f) property("composition") = forAll { boxA: Box[A] => val fG = f andThen g val mapFG: Box[A] => Box[C] = map(_)(fG) mapFG(boxA) == (mapF andThen mapG)(boxA) } // map_(h o g) o map_f == map_h o map_(g o f) property("associativity") = forAll { boxA: Box[A] => val fG = f andThen g val mapFG: Box[A] => Box[C] = map(_)(fG) val gH = g andThen h val mapGH: Box[B] => Box[D] = map(_)(gH) (mapF andThen mapGH)(boxA) == (mapFG andThen mapH)(boxA) }
  35. LIFTING: CONTEXT VS CONTENT

  36. FUNCTOR class Functor[Box[_]] { def map[A, B](boxA: Box[A]) (f: A

    => B): Box[B] }
  37. MAYBE sealed abstract class Maybe[+A] final case class Just[A](a: A)

    extends Maybe[A] case object Empty extends Maybe[Nothing]
  38. FUNCTOR FOR MAYBE implicit val maybeFunctor: Functor[Maybe] = new Functor[Maybe]

    { override def map[A, B](boxA: Maybe[A]) (f: A => B): Maybe[B] = boxA match { case Just(a) => Just(f(a)) case Empty => Empty } }
  39. BOX FUNCTION + BOX VALUES

  40. COMBINE MORE BOXES INTO ONE = APPLICATIVE

  41. APPLICATIVE'S RULES > Identity > Composition > Associativity > Homorphism

    > Interchange ...and more!
  42. SCALACHECK FTW! ApplicativeSpec extends FunctorSpec // ap(id)(a) == a property("identity")

    = forAll { box: Box[A] => ap(pureIdentity)(box) == box } // ap(pure(f))(pure(a)) == pure(f(a)) property("homorphism") = forAll { a: A => ap(pureF)(pure(a)) == pure(f(a)) } // {x => pure(x)}(a) == pure(a) property("interchange") = forAll { a: A => toPureA(a) == pure(a) } // pure(h o g o f) == ap(pure(h o g))(pure(f(a))) property("composition") = forAll { a: A => val gH = g andThen h val fGH = f andThen gH val pureGH = pure(gH) val pureFA = pure(f(a)) pure(fGH(a)) == ap(pureGH)(pureFA) }
  43. COMBINE BOXES TOGETHER > How to create a new box

    > How to combine their values together
  44. APPLICATIVE class Applicative[Box[_]] extends Functor[Box] { def pure[A](a: A): Box[A]

    def ap[A, B](boxF: Box[A => B])(boxA: Box[A]): Box[B] /*************/ def map[A, B](boxA: Box[A])(f: A => B): Box[B] = ap[A, B](pure(f))(boxA) }
  45. BOX FUNCTION + BOX VALUES

  46. APPLICATIVE class Applicative[Box[_]] extends Functor[Box] { def pure[A](a: A): Box[A]

    def ap[A, B](boxF: Box[A => B])(value: Box[A]): Box[B] def ap2[A1, A2, B](boxF: Box[(A1, A2) => B]) (value1: Box[A1], value2: Box[A2]): Box[B] // up to 22 values! // same for map }
  47. APPLICATIVE FOR MAYBE implicit val maybeApplicative: Applicative[Maybe] = new Applicative[Maybe]

    { def pure[A](a: A): Maybe[A] = Just(a) def ap[A, B](boxF: Maybe[A => B])(boxA: Maybe[A]): Maybe[B] = (boxF, boxA) match { case (Just(f), Just(a)) => pure(f(a)) case _ => Empty } }
  48. BOX IN A BOX

  49. BOX IN A BOX

  50. FUSE TWO BOXES TOGETHER = MONAD

  51. MONAD'S RULES > Identity > Composition > Associativity

  52. SCALACHECK FTW! MonadSpec extends ApplicativeSpec // flatMap(pure(a))(f(a)) == f(a) property("left

    identity") = forAll { a: A => flatMap(pure(a))(toPureFa) == toPureFa(a) } // flatMap(pure(a))(f(a)) == f(a) property("right identity") = forAll { a: A => flatMap(toPureFa(a))(pure) == toPureFa(a) } // pure(h o g o f) == ap(pure(h o g))(pure(f(a))) property("associativity") = forAll { boxA: Box[A] => val left: Box[C] = flatMap(flatMap(boxA)(toPureFa))(toPureGb) val right: Box[C] = flatMap(boxA)(a => flatMap(toPureFa(a))(toPureGb)) left == right }
  53. MONAD (AS FUNCTOR) class Monad[Box[_]] extends Functor[Box] { // map

    from Functor def flatten[A](bb: Box[Box[A]]): Box[A] /*******/ def flatMap[A, B](valueA: Box[A]) (f: A => Box[B]): Box[B] = { val bb: Box[Box[B]] = map(valueA)(f) bb.flatten } }
  54. BOXES IN A SEQUENCE

  55. FOR-COMPREHENSION val boxA: Box[A] def toBoxB: A => Box[B] def

    toBoxC: B => Box[C] def toBoxD: C => Box[D] for { a <- boxA b <- toBoxB(a) c <- toBoxC(b) d <- toBoxD(c) } yield d
  56. MONAD (AS APPLICATIVE) trait Monad[Box[_]] extends Applicative[Box] { // pure

    from Applicative def flatMap[A, B](boxA: Box[A])(f: A => Box[B]): Box[B] /******/ def flatten[A](boxBoxA: Box[Box[A]]): Box[A] = flatMap(boxBoxA)(identity) def ap[A, B](boxF: Box[A => B])(boxA: Box[A]): Box[B] = flatMap(boxF)(f => map(boxA)(f)) def map[A, B](boxA: Box[A])(f: A => B): Box[B] = flatMap(boxA)(a => pure(f(a))) }
  57. MONAD FOR MAYBE implicit val maybeMonad: Monad[Maybe] = new Monad[Maybe]

    { def flatMap[A, B](boxA: Maybe[A]) (f: (A) => Maybe[B]): Maybe[B] = boxA match { case Just(a) => f(a) case Empty => Empty } def pure[A](a: A): Maybe[A] = maybeApplicative.pure(a) }
  58. MONAD IS A MONOID IN THE CATEGORY OF ENDOFUNCTORS

  59. MONAD MONOID => pure + flatten ENDOFUNCTORS => map

  60. SUMMARY CATEGORY THEORY >> how things compose MONOID >> combining

    2 values into 1 FUNCTOR >> values lifted to a context APPLICATIVE >> independent values applied to a function in a context MONAD >> ops in sequence in a context
  61. Band BoundedSemilattice CommutativeGroup CommutativeMonoid CommutativeSemigroup Eq Group Semigroup Monoid Order

    PartialOrder Semilattice Alternative Applicative ApplicativeError Apply Bifoldable Bimonad Bitraverse Cartesian CoflatMap Comonad ContravariantCartesian FlatMap Foldable Functor Inject InvariantMonoidal Monad MonadError MonoidK NotNull Reducible SemigroupK Show ApplicativeAsk Bifunctor Contravariant Invariant Profunctor Strong Traverse Arrow Category Choice Compose Cats Type Classes kernel core/functor core/arrow core The highlighted type classes are the first ones you should learn. They’re well documented and well-known so it’s easy to get help. a |+| b a === b a =!= b a |@| b a *> b a <* b a <+> b a >>> b a <<< b a > b a >= b a < b a <= b Sync Async Effect LiftIO effect Some type classes introduce symbolic operators. NonEmptyTraverse InjectK CommutativeArrow CommutativeFlatMap CommutativeMonad ApplicativeLayer FunctorLayer ApplicativeLayerFunctor FunctorLayerFunctor ApplicativeLocal FunctorEmpty FunctorListen FunctorTell FunctorRaise MonadLayer MonadLayerFunctor MonadLayerControl MonadState TraverseEmpty Functor Applicative Monad Traverse mtl MTL type classes do not extend core type classes directly, but the effect is similar; the dashed line can be read “implies”.
  62. FORGET ABOUT THE DETAILS FOCUS ON HOW THINGS COMPOSE

  63. WANNA KNOW MORE? > Category Theory for the WH by

    @PhilipWadler > Category Theory by @BartoszMilewski > Cats-Infographics by Rob Norris - @tpolecat > Cats Documentation - Type Classes
  64. THANK YOU! > Twitter: @DanielaSfregola > Blog: danielasfregola.com github.com/DanielaSfregola/tutorial-cat