Drinking the free kool-aidDrinking the free kool-aid
David HoytDavid Hoyt
AcknowledgmentsAcknowledgments
Functional Programming in Scala
Compositional Application Architecture With
Reasonably Priced Monads
What's the problem?What's the problem?
You're not aloneYou're not alone
Took 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 theNo changes to the
original program!original program!
Another tool in your utility belt
Use 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
AgendaAgenda
1. Fundamentals
2. Code
3. Intuition
4. Code
5. Wrap-up
Design patterns andDesign patterns and
mathematical 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 monoid
Stuff 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 functor
aka the CoYonedaaka the CoYoneda
Seems uselessSeems useless
Yup.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 Trees
Expresses computation as dataExpresses computation as data
The "what"
InterpreterInterpreter
Gives 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 a
monad without having
to 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 [a
foo] 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 movie
Compositional Application Architecture
​With Reasonably Priced Monads
throw new NotImplementedError()throw new NotImplementedError()
Trampolining
Honorable mentionsHonorable mentions
Cats
Scalaz
ResourcesResources
Learning Scalaz (coproducts)
Data types à la carte
Free Monads and the Yoneda Lemma
Free Monads Are Simple
Where's the source?Where's the source?
https://github.com/davidhoyt/kool-aid
LegalLegal
xkcd

Drinking the free kool-aid