Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ScalaX 2017 - A Pragmatic Introduction to Category Theory

ScalaX 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.

Daniela Sfregola

December 13, 2017
Tweet

More Decks by Daniela Sfregola

Other Decks in Programming

Transcript

  1. A PRAGMATIC
    INTRODUCTION TO
    CATEGORY THEORY
    @DANIELASFREGOLA
    SCALAX 2017
    github.com/DanielaSfregola/tutorial-cat

    View full-size slide

  2. HELLOOOOO
    > ex Java Developer
    > OOP background
    > I am not a mathematician !

    View full-size slide

  3. I AM NOT A MATHEMATICIAN

    View full-size slide

  4. YOU DO NOT NEED TO KNOW
    CATEGORY THEORY
    TO WRITE
    GOOD CODE

    View full-size slide

  5. YOU DO NOT NEED TO KNOW
    CATEGORY THEORY
    TO WRITE
    FUNCTIONAL CODE

    View full-size slide

  6. CATEGORY THEORY
    DEEPER UNDERSTANDING ON
    OUR CODE

    View full-size slide

  7. HOW DO WE
    REASON ?

    View full-size slide

  8. COMPOSITION
    ABSTRACTION

    View full-size slide

  9. CATEGORY THEORY
    HOW THINGS COMPOSE

    View full-size slide

  10. ARROW THEORY
    CATEGORY THEORY
    HOW THINGS COMPOSE

    View full-size slide

  11. WHAT IS A CATEGORY?

    View full-size slide

  12. COMPOSITION LAW

    View full-size slide

  13. IDENTITY LAW

    View full-size slide

  14. COMPOSITION + ASSOCIATIVITY

    View full-size slide

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

    View full-size slide

  16. A PRACTICAL EXAMPLE

    View full-size slide

  17. CATEGORY WITH 1 OBJECT

    View full-size slide

  18. CATEGORY WITH 1 OBJECT
    =
    MONOID

    View full-size slide

  19. 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

    View full-size slide

  20. 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)
    }

    View full-size slide

  21. A PRACTICAL EXAMPLE

    View full-size slide

  22. MONOID
    trait Monoid[A] {
    def identity: A
    def compose(x: A, y: A): A
    }

    View full-size slide

  23. MONOID INSTANCES (1)
    implicit val intMonoid: Monoid[Int] =
    new Monoid[Int] {
    def compose(x: Int, y: Int): Int = x + y
    def identity: Int = 0
    }

    View full-size slide

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

    View full-size slide

  25. CATEGORY WITH 1+ OBJECT

    View full-size slide

  26. CATEGORY IN A BOX

    View full-size slide

  27. CATEGORY IN A BOX
    > Objects are in a Box
    > All the arrows are mapped

    View full-size slide

  28. LIFTING: CONTEXT VS CONTENT

    View full-size slide

  29. EXAMPLE OF BOXES
    > Option
    > Future
    > Try
    > List
    > Either

    View full-size slide

  30. CATEGORY IN A BOX
    =
    FUNCTOR

    View full-size slide

  31. 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)

    View full-size slide

  32. 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)
    }

    View full-size slide

  33. LIFTING: CONTEXT VS CONTENT

    View full-size slide

  34. FUNCTOR
    class Functor[Box[_]] {
    def map[A, B](boxA: Box[A])
    (f: A => B): Box[B]
    }

    View full-size slide

  35. MAYBE
    sealed abstract class Maybe[+A]
    final case class Just[A](a: A) extends Maybe[A]
    case object Empty extends Maybe[Nothing]

    View full-size slide

  36. 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
    }
    }

    View full-size slide

  37. BOX FUNCTION + BOX VALUES

    View full-size slide

  38. COMBINE MORE BOXES
    INTO ONE
    =
    APPLICATIVE

    View full-size slide

  39. APPLICATIVE'S RULES
    > Identity
    > Composition
    > Associativity
    > Homorphism
    > Interchange
    ...and more!

    View full-size slide

  40. 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)
    }

    View full-size slide

  41. COMBINE BOXES TOGETHER
    > How to create a new box
    > How to combine their values together

    View full-size slide

  42. 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)
    }

    View full-size slide

  43. BOX FUNCTION + BOX VALUES

    View full-size slide

  44. 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
    }

    View full-size slide

  45. 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
    }
    }

    View full-size slide

  46. BOX IN A BOX

    View full-size slide

  47. BOX IN A BOX

    View full-size slide

  48. FUSE TWO BOXES
    TOGETHER
    =
    MONAD

    View full-size slide

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

    View full-size slide

  50. 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
    }

    View full-size slide

  51. 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
    }
    }

    View full-size slide

  52. BOXES IN A SEQUENCE

    View full-size slide

  53. 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

    View full-size slide

  54. 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)))
    }

    View full-size slide

  55. 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)
    }

    View full-size slide

  56. FUNCTOR VS ENDOFUNCTOR

    View full-size slide

  57. MONAD IS
    A MONOID
    IN THE CATEGORY OF
    ENDOFUNCTORS

    View full-size slide

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

    View full-size slide

  59. 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

    View full-size slide

  60. 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”.

    View full-size slide

  61. FORGET ABOUT THE DETAILS
    FOCUS ON
    HOW THINGS COMPOSE

    View full-size slide

  62. WANNA KNOW MORE?
    > Category Theory for the WH by @PhilipWadler
    > Category Theory by @BartoszMilewski
    > Cats-Infographics by Rob Norris - @tpolecat
    > Cats Documentation - Type Classes

    View full-size slide

  63. THANK YOU!
    > Twitter: @DanielaSfregola
    > Blog: danielasfregola.com
    github.com/DanielaSfregola/tutorial-cat

    View full-size slide