SlideShare a Scribd company logo
Run free with
the monads!
Free Monads for fun and profit
@KenScambler
#scalamelb March 2014
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.
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)
}
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
Decision
def chooseAction(user: User,
account: Account): KVSAction = {
if (!account.suspended)
Put(user.id, account.updateSpecialOffers)
else if (account.abandoned)
Delete(user.id)
else
NoAction
}
Interpretation
def 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))
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?
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 ()
The class called “Free”
• Free is a data structure
• Tree of computations
Free[F[_], A]
The class called “Free”
• Free is a data structure
• Tree of computations
Free[F[_], A]
The class called “Free”
Suspend(F[Free[F,A]])
Return(A)
Free[F[_], A]
The class called “Free”
Suspend(F[Free[F,A]])
Return(A)
Free[F[_], A]
The class called “Free”
Suspend(F[Free[F,A]])
Return(A)
Free[F[_], A]
Why “free monads”?
Why “free monads”?
Why “free 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
Remedial interlude
Functors
• Functors are things you can map over
• F[A] => (A => B) => F[B]
trait F[A] {
def map(f: A => B): F[B]
}
Functors
trait F[A] {
def map(f: A => B): F[B]
}
Functors
trait F[A] {
def map(f: A => B): F[B]
}
Functors
trait F[A] {
def map(f: A => B): F[B]
}
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]
}
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)
}
}
}
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)
Back to our regularly
scheduled program…
“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
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
Free Blargles from
Fraxblatts
• A Fraxblatt is said to generate a Free Blargle if:
1. NO JUNK
2. NO NOISE
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:
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)
}
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] = ???
}
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[???]
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, ???]]
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, ???]]
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]]
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]
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))
}
}
Stepping through flatMap
Let’s plug in a really simple functor and see what
happens.
case class Box[A](a: A)
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))
}
banana
Return(banana)
Box(Return(banana))
Suspend(Box(Return(banana)))
that.flatMap(banana =>
Return(banana.peel))
that.flatMap(banana =>
Return(banana.peel))
that.flatMap(banana =>
Return(banana.peel))
that.flatMap(banana =>
Return(banana.peel))
liftF
Let’s automate creating the Suspend cell!
F[A] => Free[F, A]
=>
More flatmapping
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
1
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
1
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
1
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
1
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
1
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
2
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
2
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
2
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
2
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
3
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
3
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
3
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
3
for {
a <- liftF( Box(1) )
b <- liftF( Box(2) )
c <- liftF( Box(3) )
} yield a + b + c
6
Free[Box, A]
• Chain of nothings, resulting in a single value
• Not very useful!
Free[List, A]
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
1 2 3
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
1 2 3
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
1 2 3
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
1 2 3
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
1 2 3
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
1 2
2 4
3 6
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
1 2
2 4
3 6
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
1 2
2 4
3 6
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
1 2
2 4
3 6
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
for {
a <- liftF( List(1,2,3) )
b <- liftF( List(a,a*2) )
c <- liftF( Nil )
} yield a + b + c
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!
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!
Free[Function0, A]
• No-arg functions, basically a lazy value
• Flatmapping the free composes functions
• Doesn’t actually run any code
Free[Function0, A]
for {
a <- liftF(() => 2 + 3)
b <- liftF(() => a * 2)
c <- liftF(() => a * b)
} yield a + b + c
for {
a <- liftF(() => 2 + 3)
b <- liftF(() => a * 2)
c <- liftF(() => a * b)
} yield a + b + c
=> 2 + 3
for {
a <- liftF(() => 2 + 3)
b <- liftF(() => a * 2)
c <- liftF(() => a * b)
} yield a + b + c
2 + 3=>
for {
a <- liftF(() => 2 + 3)
b <- liftF(() => a * 2)
c <- liftF(() => a * b)
} yield a + b + c
2 + 3=>
for {
a <- liftF(() => 2 + 3)
b <- liftF(() => a * 2)
c <- liftF(() => a * b)
} yield a + b + c
=>
2 + 3
for {
a <- liftF(() => 2 + 3)
b <- liftF(() => a * 2)
c <- liftF(() => a * b)
} yield a + b + c
=> => 2 + 3=>
Trampolines
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
Trampolines
• Let’s take a look at some code…
Now for the power tool
Little languages
• Small, imperative DSLs
• Don’t directly do anything, can be interpreted
many ways
• Functionally pure and type-safe
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
A key-value store DSL
sealed 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]
A key-value store DSL
sealed 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
A key-value store DSL
sealed 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.
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
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
A functor for our KVS
new 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)
}
}
A functor for our KVS
new 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
A functor for our KVS
new 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
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, ()) )
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
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
The payoff
Composable scripts
def modify(key: String,
f: String => String): Free[KVS, Unit] =
for {
v <- get(key)
_ <- put(key, f(v))
} yield ()
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 ()
Pure interpreters
type 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)
Pure interpreters
type 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
Pure interpreters
type 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…
Pure interpreters
type 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…
Pure interpreters
type 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
Pure interpreters
type 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)
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
}
Effectful interpreters
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
}
Mutable map
Effectful interpreters
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
}
def go(f: F[Free[F, A]] => Free[F, A]): A
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
}
How-to summary
1. Fantasy API
2. ADT with type hole for next value
3. Functor definition for ADT
4. Lifting functions
5. Write scripts
6. Interpreter(s)
Tank game
Conclusion
• Free Monads are really powerful
• Separate decisions from interpretation, at a more
sophisticated level
• Type-safe
• Easy to use!
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!
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
Further reading
https://github.com/kenbot/free
Thank you
Hope you enjoyed hearing about Free Monads!

More Related Content

What's hot

Monadic Java
Monadic JavaMonadic Java
Monadic Java
Mario Fusco
 
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
Natan Silnitsky
 
Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)
Hermann Hueck
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and Effects
Martin Odersky
 
Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And Monad
Oliver Daff
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
Wiem Zine Elabidine
 
Functors, Applicatives and Monads In Scala
Functors, Applicatives and Monads In ScalaFunctors, Applicatives and Monads In Scala
Functors, Applicatives and Monads In Scala
Knoldus Inc.
 
Sequence and Traverse - Part 1
Sequence and Traverse - Part 1Sequence and Traverse - Part 1
Sequence and Traverse - Part 1
Philip Schwarz
 
Clean Code II - Dependency Injection
Clean Code II - Dependency InjectionClean Code II - Dependency Injection
Clean Code II - Dependency Injection
Theo Jungeblut
 
Kotlin coroutines
Kotlin coroutinesKotlin coroutines
Kotlin coroutines
Robert Levonyan
 
A Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIOA Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIO
Jorge Vásquez
 
LLVM Instruction Selection
LLVM Instruction SelectionLLVM Instruction Selection
LLVM Instruction Selection
Shiva Chen
 
Applicative style programming
Applicative style programmingApplicative style programming
Applicative style programming
José Luis García Hernández
 
44CON London - Attacking VxWorks: from Stone Age to Interstellar
44CON London - Attacking VxWorks: from Stone Age to Interstellar44CON London - Attacking VxWorks: from Stone Age to Interstellar
44CON London - Attacking VxWorks: from Stone Age to Interstellar
44CON
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
John De Goes
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Philip Schwarz
 
non-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parametersnon-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parameters
Philip Schwarz
 
Java Class Design
Java Class DesignJava Class Design
Java Class Design
Ganesh Samarthyam
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in Scala
Hermann Hueck
 
Kotlin
KotlinKotlin
Kotlin
Rory Preddy
 

What's hot (20)

Monadic Java
Monadic JavaMonadic Java
Monadic Java
 
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
How to successfully manage a ZIO fiber’s lifecycle - Functional Scala 2021
 
Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)Composing an App with Free Monads (using Cats)
Composing an App with Free Monads (using Cats)
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and Effects
 
Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And Monad
 
ZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in ScalaZIO: Powerful and Principled Functional Programming in Scala
ZIO: Powerful and Principled Functional Programming in Scala
 
Functors, Applicatives and Monads In Scala
Functors, Applicatives and Monads In ScalaFunctors, Applicatives and Monads In Scala
Functors, Applicatives and Monads In Scala
 
Sequence and Traverse - Part 1
Sequence and Traverse - Part 1Sequence and Traverse - Part 1
Sequence and Traverse - Part 1
 
Clean Code II - Dependency Injection
Clean Code II - Dependency InjectionClean Code II - Dependency Injection
Clean Code II - Dependency Injection
 
Kotlin coroutines
Kotlin coroutinesKotlin coroutines
Kotlin coroutines
 
A Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIOA Prelude of Purity: Scaling Back ZIO
A Prelude of Purity: Scaling Back ZIO
 
LLVM Instruction Selection
LLVM Instruction SelectionLLVM Instruction Selection
LLVM Instruction Selection
 
Applicative style programming
Applicative style programmingApplicative style programming
Applicative style programming
 
44CON London - Attacking VxWorks: from Stone Age to Interstellar
44CON London - Attacking VxWorks: from Stone Age to Interstellar44CON London - Attacking VxWorks: from Stone Age to Interstellar
44CON London - Attacking VxWorks: from Stone Age to Interstellar
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
 
non-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parametersnon-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parameters
 
Java Class Design
Java Class DesignJava Class Design
Java Class Design
 
Implementing the IO Monad in Scala
Implementing the IO Monad in ScalaImplementing the IO Monad in Scala
Implementing the IO Monad in Scala
 
Kotlin
KotlinKotlin
Kotlin
 

Similar to Running Free with the Monads

Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2
Hang Zhao
 
The Essence of the Iterator Pattern
The Essence of the Iterator PatternThe Essence of the Iterator Pattern
The Essence of the Iterator Pattern
Eric Torreborre
 
The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)
Eric Torreborre
 
Scala Functional Patterns
Scala Functional PatternsScala Functional Patterns
Scala Functional Patterns
league
 
ECCV2008: MAP Estimation Algorithms in Computer Vision - Part 1
ECCV2008: MAP Estimation Algorithms in Computer Vision - Part 1ECCV2008: MAP Estimation Algorithms in Computer Vision - Part 1
ECCV2008: MAP Estimation Algorithms in Computer Vision - Part 1
zukun
 
1.3- infix-ti-postfix.pdf
1.3- infix-ti-postfix.pdf1.3- infix-ti-postfix.pdf
1.3- infix-ti-postfix.pdf
soniasharmafdp
 
Allison Kaptur: Bytes in the Machine: Inside the CPython interpreter, PyGotha...
Allison Kaptur: Bytes in the Machine: Inside the CPython interpreter, PyGotha...Allison Kaptur: Bytes in the Machine: Inside the CPython interpreter, PyGotha...
Allison Kaptur: Bytes in the Machine: Inside the CPython interpreter, PyGotha...
akaptur
 
Circular queues
Circular queuesCircular queues
Circular queues
Ssankett Negi
 
Will it Blend? - ScalaSyd February 2015
Will it Blend? - ScalaSyd February 2015Will it Blend? - ScalaSyd February 2015
Will it Blend? - ScalaSyd February 2015
Filippo Vitale
 
4-Regular expression to Deterministic Finite Automata (Direct method)-05-05-2...
4-Regular expression to Deterministic Finite Automata (Direct method)-05-05-2...4-Regular expression to Deterministic Finite Automata (Direct method)-05-05-2...
4-Regular expression to Deterministic Finite Automata (Direct method)-05-05-2...
venkatapranaykumarGa
 
RxJava In Baby Steps
RxJava In Baby StepsRxJava In Baby Steps
RxJava In Baby Steps
Annyce Davis
 
Stacks.ppt
Stacks.pptStacks.ppt
Stacks.ppt
OliverKane3
 
Stacks.ppt
Stacks.pptStacks.ppt
Stacks.ppt
OliverKane3
 
Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Ruslan Shevchenko
 
Stack and queue
Stack and queueStack and queue
Stack and queue
Shakila Mahjabin
 
Data import-cheatsheet
Data import-cheatsheetData import-cheatsheet
Data import-cheatsheet
Dieudonne Nahigombeye
 
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Philip Schwarz
 
Scala Collections : Java 8 on Steroids
Scala Collections : Java 8 on SteroidsScala Collections : Java 8 on Steroids
Scala Collections : Java 8 on Steroids
François Garillot
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2
Hang Zhao
 
My lecture infix-to-postfix
My lecture infix-to-postfixMy lecture infix-to-postfix
My lecture infix-to-postfix
Senthil Kumar
 

Similar to Running Free with the Monads (20)

Fp in scala with adts part 2
Fp in scala with adts part 2Fp in scala with adts part 2
Fp in scala with adts part 2
 
The Essence of the Iterator Pattern
The Essence of the Iterator PatternThe Essence of the Iterator Pattern
The Essence of the Iterator Pattern
 
The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)The Essence of the Iterator Pattern (pdf)
The Essence of the Iterator Pattern (pdf)
 
Scala Functional Patterns
Scala Functional PatternsScala Functional Patterns
Scala Functional Patterns
 
ECCV2008: MAP Estimation Algorithms in Computer Vision - Part 1
ECCV2008: MAP Estimation Algorithms in Computer Vision - Part 1ECCV2008: MAP Estimation Algorithms in Computer Vision - Part 1
ECCV2008: MAP Estimation Algorithms in Computer Vision - Part 1
 
1.3- infix-ti-postfix.pdf
1.3- infix-ti-postfix.pdf1.3- infix-ti-postfix.pdf
1.3- infix-ti-postfix.pdf
 
Allison Kaptur: Bytes in the Machine: Inside the CPython interpreter, PyGotha...
Allison Kaptur: Bytes in the Machine: Inside the CPython interpreter, PyGotha...Allison Kaptur: Bytes in the Machine: Inside the CPython interpreter, PyGotha...
Allison Kaptur: Bytes in the Machine: Inside the CPython interpreter, PyGotha...
 
Circular queues
Circular queuesCircular queues
Circular queues
 
Will it Blend? - ScalaSyd February 2015
Will it Blend? - ScalaSyd February 2015Will it Blend? - ScalaSyd February 2015
Will it Blend? - ScalaSyd February 2015
 
4-Regular expression to Deterministic Finite Automata (Direct method)-05-05-2...
4-Regular expression to Deterministic Finite Automata (Direct method)-05-05-2...4-Regular expression to Deterministic Finite Automata (Direct method)-05-05-2...
4-Regular expression to Deterministic Finite Automata (Direct method)-05-05-2...
 
RxJava In Baby Steps
RxJava In Baby StepsRxJava In Baby Steps
RxJava In Baby Steps
 
Stacks.ppt
Stacks.pptStacks.ppt
Stacks.ppt
 
Stacks.ppt
Stacks.pptStacks.ppt
Stacks.ppt
 
Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]
 
Stack and queue
Stack and queueStack and queue
Stack and queue
 
Data import-cheatsheet
Data import-cheatsheetData import-cheatsheet
Data import-cheatsheet
 
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
 
Scala Collections : Java 8 on Steroids
Scala Collections : Java 8 on SteroidsScala Collections : Java 8 on Steroids
Scala Collections : Java 8 on Steroids
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2
 
My lecture infix-to-postfix
My lecture infix-to-postfixMy lecture infix-to-postfix
My lecture infix-to-postfix
 

More from kenbot

Grow your own tech leads
Grow your own tech leadsGrow your own tech leads
Grow your own tech leads
kenbot
 
Applied category theory: the emerging science of compositionality
Applied category theory: the emerging science of compositionalityApplied category theory: the emerging science of compositionality
Applied category theory: the emerging science of compositionality
kenbot
 
Responsible DI: Ditch the Frameworks
Responsible DI: Ditch the FrameworksResponsible DI: Ditch the Frameworks
Responsible DI: Ditch the Frameworks
kenbot
 
FP adoption at REA
FP adoption at REAFP adoption at REA
FP adoption at REA
kenbot
 
Lenses for the masses - introducing Goggles
Lenses for the masses - introducing GogglesLenses for the masses - introducing Goggles
Lenses for the masses - introducing Goggles
kenbot
 
Good functional programming is good programming
Good functional programming is good programmingGood functional programming is good programming
Good functional programming is good programming
kenbot
 
Data made out of functions
Data made out of functionsData made out of functions
Data made out of functions
kenbot
 
Imagine a world without mocks
Imagine a world without mocksImagine a world without mocks
Imagine a world without mocks
kenbot
 
2 Years of Real World FP at REA
2 Years of Real World FP at REA2 Years of Real World FP at REA
2 Years of Real World FP at REA
kenbot
 
Your data structures are made of maths!
Your data structures are made of maths!Your data structures are made of maths!
Your data structures are made of maths!
kenbot
 
Category theory for beginners
Category theory for beginnersCategory theory for beginners
Category theory for beginners
kenbot
 
The disaster of mutable state
The disaster of mutable stateThe disaster of mutable state
The disaster of mutable state
kenbot
 

More from kenbot (12)

Grow your own tech leads
Grow your own tech leadsGrow your own tech leads
Grow your own tech leads
 
Applied category theory: the emerging science of compositionality
Applied category theory: the emerging science of compositionalityApplied category theory: the emerging science of compositionality
Applied category theory: the emerging science of compositionality
 
Responsible DI: Ditch the Frameworks
Responsible DI: Ditch the FrameworksResponsible DI: Ditch the Frameworks
Responsible DI: Ditch the Frameworks
 
FP adoption at REA
FP adoption at REAFP adoption at REA
FP adoption at REA
 
Lenses for the masses - introducing Goggles
Lenses for the masses - introducing GogglesLenses for the masses - introducing Goggles
Lenses for the masses - introducing Goggles
 
Good functional programming is good programming
Good functional programming is good programmingGood functional programming is good programming
Good functional programming is good programming
 
Data made out of functions
Data made out of functionsData made out of functions
Data made out of functions
 
Imagine a world without mocks
Imagine a world without mocksImagine a world without mocks
Imagine a world without mocks
 
2 Years of Real World FP at REA
2 Years of Real World FP at REA2 Years of Real World FP at REA
2 Years of Real World FP at REA
 
Your data structures are made of maths!
Your data structures are made of maths!Your data structures are made of maths!
Your data structures are made of maths!
 
Category theory for beginners
Category theory for beginnersCategory theory for beginners
Category theory for beginners
 
The disaster of mutable state
The disaster of mutable stateThe disaster of mutable state
The disaster of mutable state
 

Recently uploaded

Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Malak Abu Hammad
 
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing InstancesEnergy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Alpen-Adria-Universität
 
Best 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERPBest 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERP
Pixlogix Infotech
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
Chart Kalyan
 
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStrDeep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
saastr
 
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
Jeffrey Haguewood
 
Presentation of the OECD Artificial Intelligence Review of Germany
Presentation of the OECD Artificial Intelligence Review of GermanyPresentation of the OECD Artificial Intelligence Review of Germany
Presentation of the OECD Artificial Intelligence Review of Germany
innovationoecd
 
Azure API Management to expose backend services securely
Azure API Management to expose backend services securelyAzure API Management to expose backend services securely
Azure API Management to expose backend services securely
Dinusha Kumarasiri
 
Finale of the Year: Apply for Next One!
Finale of the Year: Apply for Next One!Finale of the Year: Apply for Next One!
Finale of the Year: Apply for Next One!
GDSC PJATK
 
UI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentationUI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentation
Wouter Lemaire
 
WeTestAthens: Postman's AI & Automation Techniques
WeTestAthens: Postman's AI & Automation TechniquesWeTestAthens: Postman's AI & Automation Techniques
WeTestAthens: Postman's AI & Automation Techniques
Postman
 
Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024
Jason Packer
 
Skybuffer AI: Advanced Conversational and Generative AI Solution on SAP Busin...
Skybuffer AI: Advanced Conversational and Generative AI Solution on SAP Busin...Skybuffer AI: Advanced Conversational and Generative AI Solution on SAP Busin...
Skybuffer AI: Advanced Conversational and Generative AI Solution on SAP Busin...
Tatiana Kojar
 
Nunit vs XUnit vs MSTest Differences Between These Unit Testing Frameworks.pdf
Nunit vs XUnit vs MSTest Differences Between These Unit Testing Frameworks.pdfNunit vs XUnit vs MSTest Differences Between These Unit Testing Frameworks.pdf
Nunit vs XUnit vs MSTest Differences Between These Unit Testing Frameworks.pdf
flufftailshop
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
shyamraj55
 
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success StoryDriving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Safe Software
 
June Patch Tuesday
June Patch TuesdayJune Patch Tuesday
June Patch Tuesday
Ivanti
 
dbms calicut university B. sc Cs 4th sem.pdf
dbms  calicut university B. sc Cs 4th sem.pdfdbms  calicut university B. sc Cs 4th sem.pdf
dbms calicut university B. sc Cs 4th sem.pdf
Shinana2
 
Operating System Used by Users in day-to-day life.pptx
Operating System Used by Users in day-to-day life.pptxOperating System Used by Users in day-to-day life.pptx
Operating System Used by Users in day-to-day life.pptx
Pravash Chandra Das
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc
 

Recently uploaded (20)

Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdfUnlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
Unlock the Future of Search with MongoDB Atlas_ Vector Search Unleashed.pdf
 
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing InstancesEnergy Efficient Video Encoding for Cloud and Edge Computing Instances
Energy Efficient Video Encoding for Cloud and Edge Computing Instances
 
Best 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERPBest 20 SEO Techniques To Improve Website Visibility In SERP
Best 20 SEO Techniques To Improve Website Visibility In SERP
 
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdfHow to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
How to Interpret Trends in the Kalyan Rajdhani Mix Chart.pdf
 
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStrDeep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
Deep Dive: Getting Funded with Jason Jason Lemkin Founder & CEO @ SaaStr
 
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
Salesforce Integration for Bonterra Impact Management (fka Social Solutions A...
 
Presentation of the OECD Artificial Intelligence Review of Germany
Presentation of the OECD Artificial Intelligence Review of GermanyPresentation of the OECD Artificial Intelligence Review of Germany
Presentation of the OECD Artificial Intelligence Review of Germany
 
Azure API Management to expose backend services securely
Azure API Management to expose backend services securelyAzure API Management to expose backend services securely
Azure API Management to expose backend services securely
 
Finale of the Year: Apply for Next One!
Finale of the Year: Apply for Next One!Finale of the Year: Apply for Next One!
Finale of the Year: Apply for Next One!
 
UI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentationUI5 Controls simplified - UI5con2024 presentation
UI5 Controls simplified - UI5con2024 presentation
 
WeTestAthens: Postman's AI & Automation Techniques
WeTestAthens: Postman's AI & Automation TechniquesWeTestAthens: Postman's AI & Automation Techniques
WeTestAthens: Postman's AI & Automation Techniques
 
Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024Columbus Data & Analytics Wednesdays - June 2024
Columbus Data & Analytics Wednesdays - June 2024
 
Skybuffer AI: Advanced Conversational and Generative AI Solution on SAP Busin...
Skybuffer AI: Advanced Conversational and Generative AI Solution on SAP Busin...Skybuffer AI: Advanced Conversational and Generative AI Solution on SAP Busin...
Skybuffer AI: Advanced Conversational and Generative AI Solution on SAP Busin...
 
Nunit vs XUnit vs MSTest Differences Between These Unit Testing Frameworks.pdf
Nunit vs XUnit vs MSTest Differences Between These Unit Testing Frameworks.pdfNunit vs XUnit vs MSTest Differences Between These Unit Testing Frameworks.pdf
Nunit vs XUnit vs MSTest Differences Between These Unit Testing Frameworks.pdf
 
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with SlackLet's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
Let's Integrate MuleSoft RPA, COMPOSER, APM with AWS IDP along with Slack
 
Driving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success StoryDriving Business Innovation: Latest Generative AI Advancements & Success Story
Driving Business Innovation: Latest Generative AI Advancements & Success Story
 
June Patch Tuesday
June Patch TuesdayJune Patch Tuesday
June Patch Tuesday
 
dbms calicut university B. sc Cs 4th sem.pdf
dbms  calicut university B. sc Cs 4th sem.pdfdbms  calicut university B. sc Cs 4th sem.pdf
dbms calicut university B. sc Cs 4th sem.pdf
 
Operating System Used by Users in day-to-day life.pptx
Operating System Used by Users in day-to-day life.pptxOperating System Used by Users in day-to-day life.pptx
Operating System Used by Users in day-to-day life.pptx
 
TrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy SurveyTrustArc Webinar - 2024 Global Privacy Survey
TrustArc Webinar - 2024 Global Privacy Survey
 

Running Free with the Monads

  • 1. Run free with the monads! Free Monads for fun and profit @KenScambler #scalamelb March 2014
  • 2. 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.
  • 3. 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) }
  • 4. 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
  • 5. Decision def chooseAction(user: User, account: Account): KVSAction = { if (!account.suspended) Put(user.id, account.updateSpecialOffers) else if (account.abandoned) Delete(user.id) else NoAction }
  • 6. Interpretation def 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))
  • 7. 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?
  • 8. 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 ()
  • 9. The class called “Free” • Free is a data structure • Tree of computations Free[F[_], A]
  • 10. The class called “Free” • Free is a data structure • Tree of computations Free[F[_], A]
  • 11. The class called “Free” Suspend(F[Free[F,A]]) Return(A) Free[F[_], A]
  • 12. The class called “Free” Suspend(F[Free[F,A]]) Return(A) Free[F[_], A]
  • 13. The class called “Free” Suspend(F[Free[F,A]]) Return(A) Free[F[_], A]
  • 17. 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
  • 19. Functors • Functors are things you can map over • F[A] => (A => B) => F[B] trait F[A] { def map(f: A => B): F[B] }
  • 20. Functors trait F[A] { def map(f: A => B): F[B] }
  • 21. Functors trait F[A] { def map(f: A => B): F[B] }
  • 22. Functors trait F[A] { def map(f: A => B): F[B] }
  • 23. 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] }
  • 24. 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) } } }
  • 25. 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)
  • 26. Back to our regularly scheduled program…
  • 27. “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
  • 28. 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
  • 29. Free Blargles from Fraxblatts • A Fraxblatt is said to generate a Free Blargle if: 1. NO JUNK 2. NO NOISE
  • 30. 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:
  • 31. 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) }
  • 32. 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] = ??? }
  • 33. 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[???]
  • 34. 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, ???]]
  • 35. 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, ???]]
  • 36. 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]]
  • 37. 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]
  • 38. 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)) } }
  • 39. Stepping through flatMap Let’s plug in a really simple functor and see what happens. case class Box[A](a: A)
  • 40. 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)) }
  • 49. liftF Let’s automate creating the Suspend cell! F[A] => Free[F, A] =>
  • 50. More flatmapping for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c
  • 51. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 1
  • 52. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 1
  • 53. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 1
  • 54. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 1
  • 55. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 1
  • 56. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 2
  • 57. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 2
  • 58. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 2
  • 59. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 2
  • 60. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 3
  • 61. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 3
  • 62. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 3
  • 63. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 3
  • 64. for { a <- liftF( Box(1) ) b <- liftF( Box(2) ) c <- liftF( Box(3) ) } yield a + b + c 6
  • 65. Free[Box, A] • Chain of nothings, resulting in a single value • Not very useful!
  • 66. Free[List, A] for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c
  • 67. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c 1 2 3
  • 68. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c 1 2 3
  • 69. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c 1 2 3
  • 70. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c 1 2 3
  • 71. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c 1 2 3
  • 72. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c 1 2 2 4 3 6
  • 73. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c 1 2 2 4 3 6
  • 74. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c 1 2 2 4 3 6
  • 75. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c 1 2 2 4 3 6
  • 76. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c
  • 77. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c
  • 78. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c
  • 79. for { a <- liftF( List(1,2,3) ) b <- liftF( List(a,a*2) ) c <- liftF( Nil ) } yield a + b + c
  • 80. 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!
  • 81. 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!
  • 82. Free[Function0, A] • No-arg functions, basically a lazy value • Flatmapping the free composes functions • Doesn’t actually run any code
  • 83. Free[Function0, A] for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b) } yield a + b + c
  • 84. for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b) } yield a + b + c => 2 + 3
  • 85. for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b) } yield a + b + c 2 + 3=>
  • 86. for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b) } yield a + b + c 2 + 3=>
  • 87. for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b) } yield a + b + c => 2 + 3
  • 88. for { a <- liftF(() => 2 + 3) b <- liftF(() => a * 2) c <- liftF(() => a * b) } yield a + b + c => => 2 + 3=>
  • 90. 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
  • 91. Trampolines • Let’s take a look at some code…
  • 92. Now for the power tool
  • 93. Little languages • Small, imperative DSLs • Don’t directly do anything, can be interpreted many ways • Functionally pure and type-safe
  • 94. 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
  • 95. A key-value store DSL sealed 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]
  • 96. A key-value store DSL sealed 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
  • 97. A key-value store DSL sealed 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.
  • 98. 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
  • 99. 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
  • 100. A functor for our KVS new 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) } }
  • 101. A functor for our KVS new 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
  • 102. A functor for our KVS new 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
  • 103. 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, ()) )
  • 104. 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
  • 105. 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
  • 107. Composable scripts def modify(key: String, f: String => String): Free[KVS, Unit] = for { v <- get(key) _ <- put(key, f(v)) } yield ()
  • 108. 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 ()
  • 109. Pure interpreters type 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)
  • 110. Pure interpreters type 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
  • 111. Pure interpreters type 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…
  • 112. Pure interpreters type 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…
  • 113. Pure interpreters type 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
  • 114. Pure interpreters type 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)
  • 115. 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 }
  • 116. Effectful interpreters 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 } Mutable map
  • 117. Effectful interpreters 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 } def go(f: F[Free[F, A]] => Free[F, A]): A
  • 118. 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 }
  • 119. How-to summary 1. Fantasy API 2. ADT with type hole for next value 3. Functor definition for ADT 4. Lifting functions 5. Write scripts 6. Interpreter(s)
  • 121. Conclusion • Free Monads are really powerful • Separate decisions from interpretation, at a more sophisticated level • Type-safe • Easy to use!
  • 122. 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!
  • 123. 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
  • 125. Thank you Hope you enjoyed hearing about Free Monads!