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

Drinking the free kool-aid

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers. →

Drinking the free kool-aid

An introduction to the free monad and fundamental concepts required to gain intuition about its use.

Addendum: Referenced presentation title was wrong. It should be "Composable application architectures with reasonably priced monads."

Avatar for David Hoyt

David Hoyt

August 14, 2015

Other Decks in Programming

Transcript

  1. You're not alone You're not alone Took me a while

    to understand Took me a while to understand Single pass wasn't enough Single pass wasn't enough
  2. for { _ <- tell("Twitter handle(s):", "tpolecat") handles <- ask("tpolecat")

    tweets <- getTweets(handles.split(',')) done <- displayTweets(tweets) } yield done Given this program: Given this program:
  3. Why do I care? Why do I care? Purity of

    abstraction Reusable definitions Defer effects
  4. trait NaturalTransformation[F[_], G[_]] { def apply[A](given: F[A]): G[A] } type

    ~>[F[_], G[_]] = NaturalTransformation[F, G] Natural transformation Natural transformation
  5. class Option[A] class List[A] object optionToList extends (Option ~> List)

    { def apply[A](given: Option[A]): List[A] = given.toList } Option to List Option to List
  6. object stringConcatenation extends Monoid[String] { val zero: String = ""

    def append(a1: String, a2: => String): String = a1 + a2 } String concatenation String concatenation
  7. A, A, A, A, A, A Free monoid Free monoid

    Stuff this next to that. Stuff this next to that. Kind of looks like a List. Kind of looks like a List.
  8. (F[A], (A => A, A => B, B => C))

    Free functor Free functor aka the CoYoneda aka the CoYoneda
  9. trait Monad[M[_]] { def pure[A](given: A): M[A] def flatMap[A, B](given:

    M[A])(fn: A => M[B]): M[B] } trait Monad[M[_]] extends Functor[M] { def pure[A](given: A): M[A] def flatMap[A, B](given: M[A])(fn: A => M[B]): M[B] override def map[A, B](given: M[A])(fn: A => B): M[B] = flatMap(given)(a => pure(fn(a))) } Monad Monad
  10. val result: Future[Int] = for { value1 <- Future(1) value2

    <- Future(2) } yield value1 + value2 Sequencing computation Sequencing computation
  11. type Id[A] = A implicit object Identity extends Monad[Id] {

    def pure[A](given: A): Id[A] = given def map[A, B](given: Id[A])(fn: A => B): Id[B] = fn(given) def flatMap[A, B](given: Id[A])(fn: A => Id[B]): Id[B] = fn(given) } Identity monad Identity monad
  12. Repeat after me... Repeat after me... Free monad = AST

    + interpreter Free monad = AST + interpreter
  13. I have a math problem I have a math problem

    (there are several interpretations of that statement...) (yes, I mean all of them at once...) (cue the IDE already!)
  14. What's 1 + 2? What's 1 + 2? (why didn't

    you cut to the IDE?!) (don't let the audience see this!)
  15. You did what with that? You did what with that?

    That's right, I interpreted the sh** out of that.(NSFW?)
  16. case class Return[F[_], A](given: A) extends Free[F, A] case class

    FlatMap[F[_], A, B](given: Free[F, A], fn: A => Free[F, B]) extends Free[F, B] AST for monads AST for monads
  17. sealed trait Free[F[_], A] { self => def map[B](fn: A

    => B): Free[F, B] = flatMap(a => Return(fn(a))) def flatMap[B](fn: A => Free[F, B]): Free[F, B] = FlatMap(self, (a: A) => fn(a)) } Free Monad Free Monad
  18. Free Monad Free Monad sealed trait Context[A] val result: Free[Context,

    String] = for { hope <- Return[Context, String]("hope") it <- Return[Context, String]("it") works <- Return[Context, String]("works") } yield s"$hope $it $works"
  19. Free Monad Free Monad sealed trait Context[A] val result: Free[Context,

    String] = Return[Context, String]("hope") flatMap { hope => Return[Context, String]("it") flatMap { it => Return[Context, String]("works") map { works => s"$hope $it $works" } } }
  20. Which is really... Which is really... FlatMap(Return("hope"), hope => FlatMap(Return("it"),

    it => FlatMap(Return("works"), works => Return(s"$hope $it $works"))))
  21. Fs all the way down Fs all the way down

    F[F[F[F[F[F[F[F[F[F[F[F[F[F[F[F[A]]]]]]]]]]]]]]]]
  22. So what you're saying is... So what you're saying is...

    We have a monad for monads We have a monad for monads but it doesn't do monad-y things... but it doesn't do monad-y things...
  23. How is it "free"? How is it "free"? If F

    is a functor, we get a monad without having to do any extra work! sealed trait Free[F[_], A]
  24. Free as in beer? Free as in beer? Free in

    this context means generated freely in the sense that [a foo] itself doesn’t need to have any [foo] structure of its own. - Functional Programming in Scala
  25. Did he really just say "free foo"? Did he really

    just say "free foo"? Yes. Yes.
  26. Repeat after me... Repeat after me... Free monad = AST

    + interpreter Free monad = AST + interpreter
  27. Can I compose different Fs? Can I compose different Fs?

    (Great question! You read my mind!)
  28. What I mean is... What I mean is... trait Foo[A]

    trait Bar[A] val foo: Free[Foo, A] = ??? val bar: Free[Bar, A] = ??? for { _ <- foo _ <- bar } yield ()
  29. Coproducts to the rescue! Coproducts to the rescue! (honorable mention:

    monad transformers) case class Coproduct[F[_], G[_], A]()
  30. Resources Resources Learning Scalaz (coproducts) Data types à la carte

    Free Monads and the Yoneda Lemma Free Monads Are Simple