Drinking the free kool-aid

Preview:

Citation preview

Drinking the free kool-aidDrinking the free kool-aid

David HoytDavid Hoyt

AcknowledgmentsAcknowledgmentsFunctional Programming in Scala

Compositional Application Architecture WithReasonably Priced Monads

What's the problem?What's the problem?

You're not aloneYou're not aloneTook me a while to understandTook me a while to understand

Single pass wasn't enoughSingle pass wasn't enough

YawnYawn

for { _ <- tell("Twitter handle(s):", "tpolecat") handles <- ask("tpolecat") tweets <- getTweets(handles.split(',')) done <- displayTweets(tweets)} yield done

Given this program:Given this program:

Let's run it like this...Let's run it like this...

No changes to the No changes to the original program!original program!

Another tool in your utility beltUse judiciously

The free monadThe free monad

Why do I care?Why do I care?Purity of abstraction

Reusable definitions

Defer effects

Services services servicesServices services services

Wheel of doomWheel of doom®?

WTFWTF

AgendaAgenda1. Fundamentals

2. Code

3. Intuition

4. Code

5. Wrap-up

Design patterns andDesign patterns andmathematical rigormathematical rigor

trait NaturalTransformation[F[_], G[_]] { def apply[A](given: F[A]): G[A]}

type ~>[F[_], G[_]] = NaturalTransformation[F, G]

Natural transformationNatural transformation

class Option[A]class List[A]

object optionToList extends (Option ~> List) { def apply[A](given: Option[A]): List[A] = given.toList}

Option to ListOption to List

trait Monoid[A] { def zero: A def append(a1: A, a2: => A): A}

MonoidMonoid

object stringConcatenation extends Monoid[String] { val zero: String = ""

def append(a1: String, a2: => String): String = a1 + a2}

String concatenationString concatenation

A, A, A, A, A, A

Free monoidFree monoidStuff this next to that.Stuff this next to that.

Kind of looks like a List.Kind of looks like a List.

trait Functor[F[_]] { def map[A, B](fa: F[A])(fn: A => B): F[B]}

FunctorFunctor

(F[A], (A => A, A => B, B => C))

Free functorFree functoraka the CoYonedaaka the CoYoneda

Seems uselessSeems uselessYup.Yup.

Except...Except...

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

MonadMonad

val result: Future[Int] = for { value1 <- Future(1) value2 <- Future(2) } yield value1 + value2

Sequencing computationSequencing computation

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 monadIdentity monad

Repeat after me...Repeat after me...Free monad = AST + interpreterFree monad = AST + interpreter

Abstract Syntax TreesAbstract Syntax TreesExpresses computation as dataExpresses computation as data

The "what"

InterpreterInterpreterGives meaning to the dataGives meaning to the data

The "how"

I have a math problemI have a math problem(there are several interpretations of that statement...)

(yes, I mean all of them at once...)

(cue the IDE already!)

What's 1 + 2?What's 1 + 2?(why didn't you cut to the IDE?!)

(don't let the audience see this!)

You did what with that?You did what with that?That's right, I interpreted the sh** out of that. (NSFW?)

sealed trait Free[F[_], A]

Free MonadFree Monad

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 monadsAST for monads

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 MonadFree Monad

Free MonadFree 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"

Free MonadFree 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" } } }

Which is really...Which is really...

FlatMap(Return("hope"), hope => FlatMap(Return("it"), it => FlatMap(Return("works"), works => Return(s"$hope $it $works"))))

def join[A](ffa: F[F[A]]): F[A] = ???

Unable to joinUnable to join

Fs all the way downFs all the way down

F[F[F[F[F[F[F[F[F[F[F[F[F[F[F[F[A]]]]]]]]]]]]]]]]

So what you're saying is...So what you're saying is...We have a monad for monadsWe have a monad for monads

but it doesn't do monad-y things...but it doesn't do monad-y things...

Now you're getting it!Now you're getting it!

Shedding layersShedding layers

def runFree[F[_], G[_], A](given: Free[F, A]) (nat: F ~> G) (implicit G: Monad[G]): G[A]

How is it "free"?How is it "free"?

If F is a functor, we get amonad without havingto do any extra work!

sealed trait Free[F[_], A]

Free as in beer?Free as in beer?Free in this context means generated freely in the sense that [afoo] itself doesn’t need to have any [foo] structure of its own.

- Functional Programming in Scala

Did he really just say "free foo"?Did he really just say "free foo"?Yes.Yes.

Check it outCheck it out

Repeat after me...Repeat after me...Free monad = AST + interpreterFree monad = AST + interpreter

Can I compose different Fs?Can I compose different Fs?(Great question! You read my mind!)

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 ()

NoNo

Well, yes!Well, yes!

What?!What?!

Coproducts to the rescue!Coproducts to the rescue!(honorable mention: monad transformers)

case class Coproduct[F[_], G[_], A]()

Read the bookRead the book by Wouter SwierstraData types à la carte

Watch the movieWatch the movieCompositional Application Architecture

With Reasonably Priced Monads

throw new NotImplementedError()throw new NotImplementedError()Trampolining

Honorable mentionsHonorable mentionsCats

Scalaz

Where's the source?Where's the source?

https://github.com/davidhoyt/kool-aid

LegalLegalxkcd

Recommended