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

Spoiled by higher-kinded types

Spoiled by higher-kinded types

Scala is one of the few languages that have higher-kinded types; a simple feature with profound implications. This talk will explore uses of higher-kinded types and show why they are an indispensable feature.

Adelbert Chang

November 11, 2016
Tweet

More Decks by Adelbert Chang

Other Decks in Programming

Transcript

  1. Spoiled by
    higher-kinded types
    Adelbert Chang
    @adelbertchang
    Box, Inc.

    View Slide

  2. Values

    View Slide

  3. Values
    val i = 42
    val l = List((), (), ())
    val e = Right('a')

    View Slide

  4. Values
    val i = 42
    val l = List((), (), ())
    val e = Right('a')
    // Functions
    def goInt[A](int: Int): Double
    def goList[A](as: List[A]): Char
    def goEither[A, B](e: Either[A, B]): Byte

    View Slide

  5. Transforming a List
    def dup(l: List[String]): List[String] =
    l match {
    case Nil => Nil
    case h :: t => (h ++ h) :: dup(t)
    }

    View Slide

  6. Transforming a List
    def toInt(l: List[Char]): List[Int] =
    l match {
    case Nil => Nil
    case h :: t => (h.toInt) :: toInt(t)
    }

    View Slide

  7. Transforming a List
    def isZ(l: List[Int]): List[Boolean] =
    l match {
    case Nil => Nil
    case h :: t => (h == 0) :: isZ(t)
    }

    View Slide

  8. Transforming a List
    def map[A, B](as: List[A])(f: A => B): List[B] =
    as match {
    case Nil => Nil
    case h :: t => f(h) :: map(as)(t)
    }

    View Slide

  9. Transforming a List
    def dup(l: List[String]): List[String] =
    map(l)(s => s ++ s)
    def toInt(l: List[Char]): List[Int] =
    map(l)(_.toInt)
    def isZ(l: List[Int]): List[Boolean] =
    map(l)(_ == 0)

    View Slide

  10. Types

    View Slide

  11. Types
    // Kind *
    Byte
    Option[Int]
    Either[String, List[Char]]

    View Slide

  12. Types
    // Kind *
    Byte
    Option[Int]
    Either[String, List[Char]]
    // Functions
    List // * -> *
    Either // (*, *) -> *
    CanBuildFrom // (*, *, *) -> *

    View Slide

  13. Traversing a List
    def traverseFuture[A, B]
    (as: List[A])
    (f : A => Future[B]): Future[List[B]] =
    as.foldRight(Future.successful(List.empty[B])) {
    case (a, acc) =>
    f(a).zip(acc).map {
    case (b, bs) => b :: bs
    }
    }

    View Slide

  14. Traversing a List
    def traverseOption[A, B]
    (as: List[A])
    (f : A => Option[B]): Option[List[B]] =
    as.foldRight(Some(List.empty[B]): Option[List[B]]) {
    case (a, acc) =>
    f(a).zip(acc).headOption.map {
    case (b, bs) => b :: bs
    }
    }

    View Slide

  15. Traversing a List
    def traverseEither[A, B, E]
    (as: List[A])
    (f : A => Either[E, B]): Either[E, List[B]] =
    as.foldRight(Right(List()): Either[E, List[B]]) {
    case (a, acc) =>
    f(a).flatMap(a => acc.map(b => (a, b))).map {
    case (b, bs) => b :: bs
    }
    }

    View Slide

  16. Traversing a List
    trait Applicative[F[_]] {
    def pure[A](a: A): F[A]
    def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]
    def map[A, B](fa: F[A])(f: A => B): F[B]
    }
    // (* -> *) -> *
    implicit val futureAp : Applicative[Future]
    implicit val optionAp : Applicative[Option]
    implicit def eitherAp[E]: Applicative[Either[E, ?]]
    '?' syntax is via the kind-projector compiler plugin: https://github.com/non/kind-projector

    View Slide

  17. Traversing a List
    def traverse[F[_]: Applicative, A, B]
    (as: List[A])(f: A => F[B]): F[List[B]] =
    as.foldRight(List.empty[B].pure[F]) {
    case (a, acc) =>
    f(a).product(acc).map {
    case (b, bs) => b :: bs
    }
    }

    View Slide

  18. Traversing a List
    def traverseFuture[A, B]
    (as: List[A])
    (f : A => Future[B]): Future[List[B]] =
    traverse(as)(f)
    def traverseOption[A, B]
    (as: List[A])
    (f : A => Option[B]): Option[List[B]] =
    traverse(as)(f)
    def traverseEither[A, B, E]
    (as: List[A])
    (f : A => Either[E, B]): Either[E, List[B]] =
    traverse[Either[E, ?], A, B](as)(f)
    '?' syntax is via the kind-projector compiler plugin: https://github.com/non/kind-projector

    View Slide

  19. Let's write some code!

    View Slide

  20. Problems

    View Slide

  21. View Slide

  22. def foo[F[_], A](fa: F[A]): Int = 42
    val f: Function1[Int, Double] = _.toDouble
    foo(f)
    foo[Function1[Int, ?]] (f)
    foo[Function1[?, Double]](f)
    Puzzle solving
    '?' syntax is via the kind-projector compiler plugin: https://github.com/non/kind-projector

    View Slide

  23. def foo[F[_], A](fa: F[A]): Int = 42
    val f: Function1[Int, Double] = _.toDouble
    foo(f)
    foo[Function1[Int, ?]] (f)
    foo[Function1[?, Double]](f)
    Puzzle solving
    '?' syntax is via the kind-projector compiler plugin: https://github.com/non/kind-projector

    View Slide

  24. def traverse[F[_]: Applicative, A, B]
    (as: List[A])(f: A => F[B]): F[List[B]]
    def reciprocal(i: Int): Either[String, Double] =
    if (i == 0) Left("reciprocal of 0")
    else Right(1.0 / i)
    traverse(List(1, 2, 3))(reciprocal)
    Puzzle solving
    traverse[Either[String, ?], Int, Double]
    traverse[Either[?, Double], Int, String]
    '?' syntax is via the kind-projector compiler plugin: https://github.com/non/kind-projector

    View Slide

  25. def traverse[F[_]: Applicative, A, B]
    (as: List[A])(f: A => F[B]): F[List[B]]
    def reciprocal(i: Int): Either[String, Double] =
    if (i == 0) Left("reciprocal of 0")
    else Right(1.0 / i)
    traverse(List(1, 2, 3))(reciprocal)
    Puzzle solving
    traverse[Either[String, ?], Int, Double]
    traverse[Either[?, Double], Int, String]
    '?' syntax is via the kind-projector compiler plugin: https://github.com/non/kind-projector

    View Slide

  26. Currying
    val foo: (Int, String, Byte) => Long = ???

    View Slide

  27. Currying
    val foo: (Int, String, Byte) => Long = ???
    val foo: Int => String => Byte => Long = ???

    View Slide

  28. Currying
    Either[A, B] // (*, *) -> *
    Const[A, B] // (*, *) -> *
    OptionT[F[_], A] // ((* -> *), *) -> *
    Kleisli[F[_], A, B] // ((* -> *), *, *) -> *

    View Slide

  29. Currying
    Either[A][B] // * -> * -> *
    Const[A][B] // * -> * -> *
    OptionT[F[_]][A] // (* -> *) -> * -> *
    Kleisli[F[_]][A][B] // (* -> *) -> * -> * -> *

    View Slide

  30. Currying
    def traverse[F[_]: Applicative, A, B]
    (as: List[A])(f: A => F[B]): F[List[B]]

    View Slide

  31. Currying
    def traverse[F[_]: Applicative, A, B]
    (as: List[A])(f: A => F[B]): F[List[B]]
    // Either :: * -> (* -> *)
    traverse[Either[String], Int, Double]

    View Slide

  32. Currying
    def traverse[F[_]: Applicative, A, B]
    (as: List[A])(f: A => F[B]): F[List[B]]
    // Either :: * -> (* -> *)
    traverse[Either[String], Int, Double]
    List[Int] =>
    (Int => Either[String][Double]) =>
    Either[String][List[Double]]

    View Slide

  33. View Slide

  34. More code!

    View Slide

  35. • Generics of a Higher Kind
    • Higher-kinded types: the difference between giving
    up, and moving forward
    • Scala PR #5102: Add support for partial unification
    of type constructors
    • Explaining Miles's Magic
    • SI-2712: Implement higher-order unification for type
    constructor inference
    Further Reading

    View Slide

  36. EOF

    View Slide