running free with the monads

125
Run free with the monads! Free Monads for fun and profit @KenScambler #scalamelb March 2014

Upload: kenbot

Post on 07-May-2015

4.277 views

Category:

Technology


0 download

DESCRIPTION

Free Monads are a powerful technique that can separate the representation of programs from the messy details of how they get run. I'll go into the details of how they work, how to use them for fun and profit in your own code, and demonstrate a live Free Monad-driven tank game. Supporting code at https://github.com/kenbot/free

TRANSCRIPT

Page 1: Running Free with the Monads

Run free with the monads!

Free Monads for fun and profit

@KenScambler#scalamelb March 2014

Page 2: Running Free with the Monads

The problem• Separation of concerns is paramount to software• In FP, we try to banish effects to the peripheries

of our programs• Results and decisions must be represented as

data, such as ADTs• Interpretation can happen later• Not super expressive though.

Page 3: Running Free with the Monads

Decision/Interpretation

(tangled)def updateAccount(user: User, db: KVStore): Unit = { val account = db.get(user.id)

if (!account.suspended) db.put(user.id, account.updateSpecialOffers) else if (account.abandoned) db.delete(user.id)}

Page 4: Running Free with the Monads

Decisions as data

sealed trait KVSAction

case class Put(key: String, value: String) extends KVSAction

case class Delete(key: String) extends KVSAction

case object NoAction extends KVSAction

Page 5: Running Free with the Monads

Decisiondef chooseAction(user: User, account: Account): KVSAction = {

if (!account.suspended) Put(user.id, account.updateSpecialOffers) else if (account.abandoned) Delete(user.id) else NoAction}

Page 6: Running Free with the Monads

Interpretationdef interpret(action: KVSAction): Unit = { action match { case Put(key, value) => db.put(key, value) case Delete(key) => db.delete(key) case NoAction => () }}

val account = db.get(bob,id)interpret(chooseAction(bob, account))

Page 7: Running Free with the Monads

How far can we push it?

• Can our pure “decision” data be as sophisticated as a program?

• Can we create DSLs that can be run later in different ways?

• Can we manipulate & rewrite our “program” on the fly?

• Conditional logic?• Loops?• Coroutines?

Page 8: Running Free with the Monads

How far can we push it?

def updateAccount(user: User): Unit = for { account <- getAccount(user.id) _ <- when(!account.suspended)( put(user.id, user.updated)) _ <- when(account.abandoned)( delete(user.id)) } yield ()

Page 9: Running Free with the Monads

The class called “Free”

• Free is a data structure• Tree of computations

Free[F[_], A]

Page 10: Running Free with the Monads

The class called “Free”

• Free is a data structure• Tree of computations

Free[F[_], A]

Page 11: Running Free with the Monads

The class called “Free”

Suspend(F[Free[F,A]])

Return(A)

Free[F[_], A]

Page 12: Running Free with the Monads

The class called “Free”

Suspend(F[Free[F,A]])

Return(A)

Free[F[_], A]

Page 13: Running Free with the Monads

The class called “Free”

Suspend(F[Free[F,A]])

Return(A)

Free[F[_], A]

Page 14: Running Free with the Monads

Why “free monads”?

Page 15: Running Free with the Monads

Why “free monads”?

Page 16: Running Free with the Monads

Why “free monads”?

Page 17: Running Free with the Monads

Why “free monads”?If F[_] is a functor, Free is a

monad…… for free!

• This buys us a whole world of existing functionality

• Better abstraction• Sequential computations• Elegant imperative-style syntax

Page 18: Running Free with the Monads

Remedial interlude

Page 19: Running Free with the Monads

Functors• Functors are things you can map over• F[A] => (A => B) => F[B]

trait F[A] { def map(f: A => B): F[B]}

Page 20: Running Free with the Monads

Functorstrait F[A] { def map(f: A => B): F[B]}

Page 21: Running Free with the Monads

Functorstrait F[A] { def map(f: A => B): F[B]}

Page 22: Running Free with the Monads

Functorstrait F[A] { def map(f: A => B): F[B]}

Page 23: Running Free with the Monads

Monads• Monads have a flatMap method that allows you to

chain computations together sequentially

class M[A] { def map(f: A => B): M[B] def flatMap(f: A => M[B]): M[B]}

Page 24: Running Free with the Monads

Monads• Nesting flatmaps allows sequential actions,

ignoring the specific context!

nbaTeams.flatMap { team => team.players.flatMap { player => player.gamesPlayed.map { game => BasketballCard(team, player, game) } }}

Page 25: Running Free with the Monads

Monads• Neat comprehension syntax in Scala and Haskell• Makes it look like a regular program

for { team <- nbaTeams player <- team.players game <- player.gamesPlayed} yield BasketballCard(team, player, game)

Page 26: Running Free with the Monads

Back to our regularly scheduled program…

Page 27: Running Free with the Monads

“Free objects” in maths

• Important concept in maths!• Many free structures in Category Theory• Free Monoids, Free Monads, Free Categories, Free

Groups, etc• It only counts as “free” if the free thing gets

generated in the simplest possible way

Page 28: Running Free with the Monads

Free Blargles from Fraxblatts

• A Fraxblatt is said to generate a Free Blargle if:

1. The Blargle doesn’t contain anything not directly produced from a Fraxblatt

2. The Blargle doesn’t contain anything beyond what it needs to be a Blargle

Page 29: Running Free with the Monads

Free Blargles from Fraxblatts

• A Fraxblatt is said to generate a Free Blargle if:

1. NO JUNK

2. NO NOISE

Page 30: Running Free with the Monads

Making an honest monad of it

case class Return[F[_], A](a: A) extends Free[F, A] {

def flatMap(f: A => Free[F, B]): Free[F, B] = ???

}

• Define flatMap for Return:

Page 31: Running Free with the Monads

Making an honest monad of it

case class Return[F[_], A](a: A) extends Free[F, A] {

def flatMap(f: A => Free[F, B]): Free[F, B] = f(a)

}

Page 32: Running Free with the Monads

Making an honest monad of it

• Define flatMap for Suspend:

case class Suspend[F[_], A](next: F[Free[F,A]]) extends Free[F, A] {

def flatMap(f: A => Free[F, B]): Free[F, B] = ???}

Page 33: Running Free with the Monads

Making an honest monad of it

• We need to map over the functor

case class Suspend[F[_], A](next: F[Free[F,A]]) extends Free[F, A] {

def flatMap(f: A => Free[F, B]): Free[F, B] = { F??? map ??? }}

F[???]

Page 34: Running Free with the Monads

Making an honest monad of it

• “next” is the only F we have lying around

case class Suspend[F[_], A](next: F[Free[F,A]]) extends Free[F, A] {

def flatMap(f: A => Free[F, B]): Free[F, B] = { next map {free => ???} }}

F[Free[F, ???]]

Page 35: Running Free with the Monads

Making an honest monad of it

• flatMap is almost the only thing we can do to a Free

case class Suspend[F[_], A](next: F[Free[F,A]]) extends Free[F, A] {

def flatMap(f: A => Free[F, B]): Free[F, B] = { next map {free => free.flatMap(???)} }}

F[Free[F, ???]]

Page 36: Running Free with the Monads

Making an honest monad of it

• Mapping function f will turn our As into Free[F, B]s

case class Suspend[F[_], A](next: F[Free[F,A]]) extends Free[F, A] {

def flatMap(f: A => Free[F, B]): Free[F, B] = { next map {free => free.flatMap(f)} }}

F[Free[F, B]]

Page 37: Running Free with the Monads

Making an honest monad of it

• Wrapping in Suspend matches the type signature!

case class Suspend[F[_], A](next: F[Free[F,A]]) extends Free[F, A] {

def flatMap(f: A => Free[F, B]): Free[F, B] = { Suspend(next map {free => free.flatMap(f)}) }}

Free[F, B]

Page 38: Running Free with the Monads

Making an honest monad of it

• Cleaning up the syntax a bit…

case class Suspend[F[_], A](next: F[Free[F,A]]) extends Free[F, A] {

def flatMap(f: A => Free[F, B]): Free[F, B] = { Suspend(next map (_ flatMap f)) }}

Page 39: Running Free with the Monads

Stepping through flatMap

Let’s plug in a really simple functor and see what happens.

case class Box[A](a: A)

Page 40: Running Free with the Monads

Stepping through flatMap

Let’s plug in a really simple functor and see what happens.

case class Box[A](a: A) { def map[B](f: A => B) = Box(f(a))}

Page 41: Running Free with the Monads

banana

Page 42: Running Free with the Monads

Return(banana)

Page 43: Running Free with the Monads

Box(Return(banana))

Page 44: Running Free with the Monads

Suspend(Box(Return(banana)))

Page 45: Running Free with the Monads

that.flatMap(banana => Return(banana.peel))

flatMap

Page 46: Running Free with the Monads

that.flatMap(banana => Return(banana.peel))

map

Page 47: Running Free with the Monads

that.flatMap(banana => Return(banana.peel))

flatMap

Page 48: Running Free with the Monads

that.flatMap(banana => Return(banana.peel))

Page 49: Running Free with the Monads

liftFLet’s automate creating the Suspend cell!

F[A] => Free[F, A]

=>

Page 50: Running Free with the Monads

More flatmapping

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

Page 51: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

1

Page 52: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

liftF

1

Page 53: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

flatMap

1

Page 54: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

map 1

Page 55: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

flatMap

1

Page 56: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

2lift

F

Page 57: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

2flatMap

Page 58: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

2

ma

p

Page 59: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

2flatMap

Page 60: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

3

liftF

Page 61: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

3

flatMap

Page 62: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

3

map

Page 63: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

3

flatMap

Page 64: Running Free with the Monads

for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) )} yield a + b + c

6

Page 65: Running Free with the Monads

Free[Box, A]• Chain of nothings, resulting in a single value• Not very useful!

Page 66: Running Free with the Monads

Free[List, A]

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

Page 67: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

1 2 3

Page 68: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

1 2 3

Page 69: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

1 2 3 flatMap

Page 70: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

1 2 3 map

Page 71: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

1 2 3 flatmap

Page 72: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

liftF1 2 2 4

3 6

Page 73: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

flatMap

1 2 2 43 6

Page 74: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

map1 2 2 4

3 6

Page 75: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

flatMap

1 2 2 43 6

Page 76: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

liftF

Page 77: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

flatMap

Page 78: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

map

Page 79: Running Free with the Monads

for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil )} yield a + b + c

abort

Page 80: Running Free with the Monads

Free[List,A]• Branching tree shape, with data at the leaves• Empty lists can terminate the tree, not just

Return.• Again, not super useful.

The functor controls the branching factor!

Page 81: Running Free with the Monads

Funky functions

• Functors are not just data structures that hold values

• They are computations!• Free’s real power is unleashed when the Functor

maps over functions!

Page 82: Running Free with the Monads

Free[Function0, A]• No-arg functions, basically a lazy value• Flatmapping the free composes functions• Doesn’t actually run any code

Page 83: Running Free with the Monads

Free[Function0, A]

for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b)} yield a + b + c

Page 84: Running Free with the Monads

for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b)} yield a + b + c

=> 2 + 3

Page 85: Running Free with the Monads

for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b)} yield a + b + c

liftF

2 + 3

=>

Page 86: Running Free with the Monads

for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b)} yield a + b + c

flatMap

2 + 3

=>

Page 87: Running Free with the Monads

for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b)} yield a + b + c

map=>2 +

3

Page 88: Running Free with the Monads

for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b)} yield a + b + c

compos

e

=> =>2 + 3=>

Page 89: Running Free with the Monads

Trampolines

Page 90: Running Free with the Monads

Trampolines• Believe it or not, Free[Function0,A] is incredibly

useful!• Also known as Trampoline[A]• Moves tail calls onto the heap, avoiding stack

overflows• The best we can get for mutual tail recursion on

the JVM

Page 91: Running Free with the Monads

Trampolines• Let’s take a look at some code…

Page 92: Running Free with the Monads

Now for the power tool

Page 93: Running Free with the Monads

Little languages• Small, imperative DSLs• Don’t directly do anything, can be interpreted

many ways• Functionally pure and type-safe

Page 94: Running Free with the Monads

A key-value store DSL• A bit like the KVSAction ADT way back at the start• There’s a “type hole” for the next thing• That means…. we can make it a Functor!• Mechanical translation from corresponding API

functions

Page 95: Running Free with the Monads

A key-value store DSLsealed trait KVS[Next]

case class Put[Next](key: String, value: String, next: Next) extends KVS[Next]

case class Delete[Next](key: String, next: Next) extends KVS[Next]

case class Get[Next](key: String, onValue: String => Next) extends KVS[Next]

Page 96: Running Free with the Monads

A key-value store DSLsealed trait KVS[Next]

case class Put[Next](key: String, value: String, next: Next) extends KVS[Next]

case class Delete[Next](key: String, next: Next) extends KVS[Next]

case class Get[Next](key: String, onValue: String => Next) extends KVS[Next]

Just have a slot for the next thing, if we don’t care about a

result value

Page 97: Running Free with the Monads

A key-value store DSLsealed trait KVS[Next]

case class Put[Next](key: String, value: String, next: Next) extends KVS[Next]

case class Delete[Next](key: String, next: Next) extends KVS[Next]

case class Get[Next](key: String, onValue: String => Next) extends KVS[Next]

Have a Result => Next function, if we

want to “return” some Result.

Page 98: Running Free with the Monads

Which looks a bit like…

def put[A](key: String, value: String): Unit

def delete[A](key: String): Unit

def get[A](key: String): String

Page 99: Running Free with the Monads

Which is a bit like…def put[A](key: String, value: String): Unit

def delete[A](key: String): Unit

def get[A](key: String): String

Page 100: Running Free with the Monads

A functor for our KVSnew Functor[KVS] { def map[A,B](kvs: KVS[A])(f: A => B): KVS[B] = kvs match {

case Put(key, value, next) => Put(key, value, f(next))

case Delete(key, next) => Delete(key, f(next))

case Get(key, onResult) => Get(key, onResult andThen f)

} }

Page 101: Running Free with the Monads

A functor for our KVSnew Functor[KVS] { def map[A,B](kvs: KVS[A])(f: A => B): KVS[B] = kvs match {

case Put(key, value, next) => Put(key, value, f(next))

case Delete(key, next) => Delete(key, f(next))

case Get(key, onResult) => Get(key, onResult andThen f)

} }

To map over the next value, just apply f

Page 102: Running Free with the Monads

A functor for our KVSnew Functor[KVS] { def map[A,B](kvs: KVS[A])(f: A => B): KVS[B] = kvs match {

case Put(key, value, next) => Put(key, value, f(next))

case Delete(key, next) => Delete(key, f(next))

case Get(key, onResult) => Get(key, onResult andThen f)

} }

To map over a function yielding the next value, compose

f with it

Page 103: Running Free with the Monads

Lifting into the Free Monad

def put(key: String, value: String): Free[KVS, Unit] = liftF( Put(key, value, ()) ) def get(key: String): Free[KVS, String] = liftF( Get(key, identity) ) def delete(key: String): Free[KVS, Unit] = liftF( Delete(key, ()) )

Page 104: Running Free with the Monads

Lifting into the Free Monad

def put(key: String, value: String): Free[KVS, Unit] = liftF( Put(key, value, ()) ) def get(key: String): Free[KVS, String] = liftF( Get(key, identity) ) def delete(key: String): Free[KVS, Unit] = liftF( Delete(key, ()) )

Initialise with Unit, when we

don’t care about the value

Page 105: Running Free with the Monads

Lifting into the Free Monad

def put(key: String, value: String): Free[KVS, Unit] = liftF( Put(key, value, ()) ) def get(key: String): Free[KVS, String] = liftF( Get(key, identity) ) def delete(key: String): Free[KVS, Unit] = liftF( Delete(key, ()) )

Initialise with the identity function, when we want to return a value

Page 106: Running Free with the Monads

The payoff

Page 107: Running Free with the Monads

Composable scriptsdef modify(key: String, f: String => String): Free[KVS, Unit] = for { v <- get(key) _ <- put(key, f(v)) } yield ()

Page 108: Running Free with the Monads

Harmless imperative code

val script: Free[KVS, Unit] = for { id <- get(“swiss-bank-account-id”) _ <- modify(id, (_ + 1000000)) _ <- put(“bermuda-airport”, “getaway car”) _ <- delete(“tax-records”) } yield ()

Page 109: Running Free with the Monads

Pure interpreterstype KVStore = Map[String, String]

def interpretPure(kvs: Free[KVS, Unit], table: KVStore): KVStore = kvs.resume.fold({ case Get(key, onResult) => interpretPure(onResult(table(key)), table)

case Put(key, value, next) => interpretPure(next, table + (key -> value))

case Delete(key, next) => interpretPure(next, table - key)

}, _ => table)

Page 110: Running Free with the Monads

Pure interpreterstype KVStore = Map[String, String]

def interpretPure(kvs: Free[KVS, Unit], table: KVStore): KVStore = kvs.resume.fold({ case Get(key, onResult) => interpretPure(onResult(table(key)), table)

case Put(key, value, next) => interpretPure(next, table + (key -> value))

case Delete(key, next) => interpretPure(next, table - key)

}, _ => table)

KVStore is immutable

Page 111: Running Free with the Monads

Pure interpreterstype KVStore = Map[String, String]

def interpretPure(kvs: Free[KVS, Unit], table: KVStore): KVStore = kvs.resume.fold({ case Get(key, onResult) => interpretPure(onResult(table(key)), table)

case Put(key, value, next) => interpretPure(next, table + (key -> value))

case Delete(key, next) => interpretPure(next, table - key)

}, _ => table)

F[Free[F, A]] \/ A

Resume and fold…

Page 112: Running Free with the Monads

Pure interpreterstype KVStore = Map[String, String]

def interpretPure(kvs: Free[KVS, Unit], table: KVStore): KVStore = kvs.resume.fold({ case Get(key, onResult) => interpretPure(onResult(table(key)), table)

case Put(key, value, next) => interpretPure(next, table + (key -> value))

case Delete(key, next) => interpretPure(next, table - key)

}, _ => table)

KVS[Free[KVS, Unit]] \/ Unit

Resume and fold…

Page 113: Running Free with the Monads

Pure interpreterstype KVStore = Map[String, String]

def interpretPure(kvs: Free[KVS, Unit], table: KVStore): KVStore = kvs.resume.fold({ case Get(key, onResult) => interpretPure(onResult(table(key)), table)

case Put(key, value, next) => interpretPure(next, table + (key -> value))

case Delete(key, next) => interpretPure(next, table - key)

}, _ => table)

When resume finally returns Unit, return the table

Page 114: Running Free with the Monads

Pure interpreterstype KVStore = Map[String, String]

def interpretPure(kvs: Free[KVS, Unit], table: KVStore): KVStore = kvs.resume.fold({ case Get(key, onResult) => interpretPure(onResult(table(key)), table)

case Put(key, value, next) => interpretPure(next, table + (key -> value))

case Delete(key, next) => interpretPure(next, table - key)

}, _ => table)

Page 115: Running Free with the Monads

Effectful interpreter(s)type KVStore = mutable.Map[String, String]

def interpretImpure(kvs: Free[KVS,Unit], table: KVStore): Unit = kvs.go { case Get(key, onResult) => onResult(table(key))

case Put(key, value, next) => table += (key -> value) next

case Delete(key, next) => table -= key next }

Page 116: Running Free with the Monads

Effectful interpreterstype KVStore = mutable.Map[String, String]

def interpretImpure(kvs: Free[KVS,Unit], table: KVStore): Unit = kvs.go { case Get(key, onResult) => onResult(table(key))

case Put(key, value, next) => table += (key -> value) next

case Delete(key, next) => table -= key next }

Mutable map

Page 117: Running Free with the Monads

Effectful interpreterstype KVStore = mutable.Map[String, String]

def interpretImpure(kvs: Free[KVS,Unit], table: KVStore): Unit = kvs.go { case Get(key, onResult) => onResult(table(key))

case Put(key, value, next) => table += (key -> value) next

case Delete(key, next) => table -= key next }

def go(f: F[Free[F, A]] => Free[F, A]): A

Page 118: Running Free with the Monads

Effectful interpreter(s)type KVStore = mutable.Map[String, String]

def interpretImpure(kvs: Free[KVS,Unit], table: KVStore): Unit = kvs.go { case Get(key, onResult) => onResult(table(key))

case Put(key, value, next) => table += (key -> value) next

case Delete(key, next) => table -= key next }

Page 119: Running Free with the Monads

How-to summary1. Fantasy API2. ADT with type hole for next value3. Functor definition for ADT4. Lifting functions5. Write scripts6. Interpreter(s)

Page 120: Running Free with the Monads

Tank game

Page 121: Running Free with the Monads

Conclusion• Free Monads are really powerful• Separate decisions from interpretation, at a more

sophisticated level• Type-safe• Easy to use!

Page 122: Running Free with the Monads

Conclusion• Express your decisions in a “little language”• Pause and resume programs, co-routine style• Rewrite programs macro-style• Avoid stack overflows with Trampolines

This is a great tool to have in your toolkit!

Page 123: Running Free with the Monads

Further reading• Awodey, Category Theory• Bjarnason, Dead Simple Dependency Injection• Bjarnason, Stackless Scala with Free Monads• Doel, Many roads to Free Monads• Ghosh, A Language and its Interpretation:

Learning Free Monads• Gonzalez, Why Free Monads Matter• Haskell.org, Control.Monad.Free• Perrett, Free Monads, Part 1• Scalaz, scalaz.Free

Page 124: Running Free with the Monads

Further reading

https://github.com/kenbot/free

Page 125: Running Free with the Monads

Thank youHope you enjoyed hearing about Free Monads!