Upload
david-hoyt
View
417
Download
0
Tags:
Embed Size (px)
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
ResourcesResourcesLearning 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
LegalLegalxkcd