SlideShare a Scribd company logo
Functional I/O and Effects
Or, if a Monad folds in a functional forest, does it make an effect?
Dylan Forciea
Oseberg
February 28, 2017
Topics
• What does it mean for a language to be functional?
• Creating a type to represent IO operations
• Adding monoid operations to the IO type
• Adding monad operations to the IO type
• How a naïve approach for executing the monad is insufficient, and how
trampolines can fix it
What is a functional programming language?
• Functions as first class objects?
• Declarative rather than imperative?
• Recursion rather than loops?
ReferentialTransparency
• Yes!
• But ReferentialTransparency is also key to some advantages of functional
programming
• Any expression can be replaced with its value without changing the
meaning of the program
• This alludes to the fact that you should avoid side effects
Example of ReferentialTransparency
def average(x: Int, y: Int, z: Int): Int = (x + y + z) / 3
average(1, 2, 3)
 (1 + 2 + 3) / 3
 (6) / 3
 2
Running this a second time with the same input values will always return the
same result
Counterexample of ReferentialTransparency
var cumulativeValue: Int = 0
var numberSummed: Int = 0
def cumulativeAverage(x: Int): Int = {
numberSummed = numberSummed + 1
cumulativeValue += x
cumulativeValue / numberSummed
}
cumulativeAverage(10)
 cumulativeValue = 10, numberSummed = 1, result = (10/1) = 10
cumulativeAverage(20)
 cumulativeValue = 30, numberSummed = 2, result = (30/2) = 15
cumulativeAverage(20)
 cumulativeValue = 50, numberSummed = 3, result = (50/3) = 16
What does ReferentialTransparency buy you?
• Testability
• Promotes parallel programming
• Can enable optimization of code
ReferentialTransparency and IO
def addOneToUserInput(): Int = scala.io.StdIn.readInt() + 1
The return value of executing this function will be different depending on what
the user inputs!
Wrapping IO in aType
• Separate IO into a type and define actions inside
• Denotes which pieces of the code are actions
• Defer running an action until later
• Describe a program containing effects purely functionally, and then run it
Wrapping IO in aType
trait IO[A] { def run: A }
def PrintLine(msg: String): IO[Unit] = new IO[Unit] { def run: Unit =
println(msg) }
PrintLine(“Hello, world!”).run
 Hello, world!
How do we run a sequence of these actions?
• Make it a monoid, of course!
• What was a monoid, again?
Monoid Refresher
• A monoid is a combination of:
• Some type, A
• A seed (or "zero") for that type
• An associative binary operator.
• Concretely:
trait Monoid[A] {
def op(a1:A, a2: A): A
def zero: A
}
• In addition to the associativity of op, for any A the following must hold:
• op(a, zero) == a == op(zero, a)
Monoid Refresher Example
• For integer addition:
object IntMonoid extends Monoid[Int] {
def op(a: Int, b: Int): Int = a + b
def zero: Int = 0
}
• You can use operations like fold to combine these:
• List(1, 2, 3).fold(IntMonoid.zero)(IntMonoid.op)
• (((0 + 1) + 2) + 3) = 6
IO as a Monoid
trait IO[A] { self =>
def run:A
def ++[B](io: IO[B]): IO[B] = new IO[B] { def run: B = { self.run; io.run } }
}
object IO {
def zero: IO[Unit] = new IO[Unit] { def run: Unit = { } }
def ++[A, B](io1: IO[A], io2: IO[B]): IO[B] = io1 ++ io2
}
IO Monoid Example Usage
(PrintLine("Hello!") ++ PrintLine("World!")).run
Hello!
World!
val ioList = List(PrintLine("One"), PrintLine("Two"), PrintLine("Three"))
ioList.fold(IO.zero)(IO.++).run
One
Two
Three
That’s great, but…
• We can chain together output effects
• But, how can we actually perform any operations on input effects?
• Monads!
• Wait, another one of those big words…
Monad refresher
• Monads provide two operations:
• def map[B](f:A => B): IO[B]
• def flatMap[B](f:A => IO[B]): IO[B]
Monad refresher
• map transforms a value from domainA to a value from domain B inside the
monad
• flatMap transforms a value from domain A into a Monad containing a value
from domain B, and then returns a monad containing a value from domain B
How does this help?
• map takes the result of an IO monad and perform computations on the value
• flatMap takes the result of an IO monad, and then perform IO for output
• Like control flow for the IO “program”
IO as a Monad
trait IO[A] { self =>
def run:A
def ++[B](io: IO[B]): IO[B] = new IO[B] { def run: B = { self.run; io.run } }
def map[B](f: A => B): IO[B] = new IO[B] { def run: B = f(self.run) }
def flatMap[B](f: A => IO[B]): IO[B] =
new IO[B] { def run: B = f(self.run).run }
}
def PrintLine(msg: String): IO[Unit] =
new IO[Unit] { def run: Unit = println(msg) }
def GetLine(): IO[String] =
new IO[String] { def run: String = scala.io.StdIn.readLine() }
Lets put it together!
PrintLine("Type a number")
.flatMap(_ => GetLine())
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
.run
Type a number
 1
2
Can’t mix and match
GetLine().toInt + 1
<console>:15: error: value toInt is not a member of IO[String]
GetLine().toInt + 1
Some useful operations
def doWhile[A](a: IO[A])(cond: A => IO[Boolean]): IO[Unit] = for {
a1 <- a
ok <- cond(a1)
_ <- if (ok) doWhile(a)(cond) else IO.zero
} yield ()
def forever[A, B](a: IO[A]): IO[B] = a flatMap(_ => forever(a))
Houston, we have a problem!
forever(PrintLine("Hello")).run
Hello
Hello
…
<Stack Overflow!>
• What happened?!
Not tail recursive
def flatMap[B](f: A => IO[B]): IO[B] = new IO[B] { def run: B = f(self.run).run }
def forever[B](a: IO[A]): IO[B] = a flatMap(_ => forever(a))
• forever keeps on calling flatMap
• Every time flatMap is called, we end up one level lower in recursion
• Since the function definition says we need to keep track of the results of
f(this.run) to call run on it, we keep on adding on to the call stack every time
How do we fix this?
• Use Trampolining
• Create a series ofAlgebraic DataTypes (ADTs) to describe how the
operation will run
• We can make running our IO monad operations tail recursive
• First things first…
• What is tail recursion?
• What is anADT?
Tail Recursion
• Don’t want to keep track of state after calling function recursively
• If the last function run is the recursive call, then it is in tail position
• We can skip adding a stack frame
Tail recursion example
def factorial(n: BigInt): BigInt = if (n == 0) 1 else n * factorial(n-1)
def factorial_tailrec(n: BigInt): BigInt = {
def factorial1(n: BigInt, acc: BigInt): BigInt =
if (n == 0)
acc
else
factorial1(n - 1, n * acc)
factorial1(n, 1)
}
Algebraic DataTypes
• This is a data type that can be one of several things, and each thing can
contain a defined set of data
• We can use pattern matching to perform operations on the ADT
• This is probably more apparent if we just give an example…
List ADT definition
sealed trait List[+A]
case class Cons[+A](head: A, tail: List[A]) extends List[A]
case object Nil extends List[Nothing]
List ADT Pattern Matching
val a = Cons(1, Nil)
val b = Cons(2, a)
val c = Cons(3, b)
def add(list: List[Int]): Int =
list match {
case Cons(head, tail) => head + add(tail)
case Nil => 0
}
add(c)
6 3 2 1
Describing IO monad operations with an ADT
sealed trait IO[A] { self =>
def flatMap[B](f: A => IO[B]): IO[B] = FlatMap(self, f)
def map[B](f: A => B): IO[B] = flatMap(a => (Return(f(a))))
}
case class Return[A](a: A) extends IO[A]
case class Suspend[A](resume: () =>A) extends IO[A]
case class FlatMap[A, B](sub: IO[A], k: A => IO[B]) extends IO[B]
def PrintLine(s: String): IO[Unit] = Suspend(() => Return(println(s)))
def GetLine: IO[String] = Suspend(() => scala.io.StdIn.readLine())
Our example from earlier!
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Suspend
PrintLine
Prompt
Our example from earlier!
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Suspend
PrintLine
Prompt
FlatMap
Suspend
GetLine
Our example from earlier!
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Suspend
PrintLine
Prompt
FlatMap
Suspend
GetLine
Return
s.toInt
FlatMap
Our example from earlier!
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Suspend
PrintLine
Prompt
FlatMap
Suspend
GetLine
Return
s.toInt
Return
i + 1
FlatMap
FlatMap
Our example from earlier!
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Suspend
PrintLine
Prompt
FlatMap
Suspend
GetLine
Return
s.toInt
Return
i + 1
Suspend
PrintLine
Result
FlatMap
FlatMap
FlatMap
Now we need the Interpreter…
def run[A](io: IO[A]): A = io match {
case Return(a) => a
case Suspend(r) => r()
case FlatMap(x, f) => x match {
case Return(a) => run(f(a))
case Suspend(r) => run(f(r()))
case FlatMap(y, g) => run(y.flatMap(a => g(a).flatMap(f)))
}
}
Run the example
Suspend
PrintLine
Prompt
FlatMap
Suspend
GetLine
Return
s.toInt
Return
i + 1
Suspend
PrintLine
Result
FlatMap
FlatMap
FlatMap
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Run the example
Suspend
PrintLine
Prompt
FlatMap
Suspend
GetLine
Return
s.toInt
Return
i + 1
Suspend
PrintLine
Result
FlatMap
FlatMap FlatMap
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Run the example
Suspend
PrintLine
Prompt
FlatMap
Suspend
GetLine
Return
s.toInt
Return
i + 1
Suspend
PrintLine
Result
FlatMap
FlatMap
FlatMap
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Run the example
Suspend
PrintLine
Prompt
FlatMap
Suspend
GetLine
Return
s.toInt
Return
i + 1
Suspend
PrintLine
Result
FlatMap
FlatMap
FlatMap
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Run the example
Suspend
PrintLine
Prompt
FlatMap
Suspend
GetLine
Return
s.toInt
Return
i + 1
Suspend
PrintLine
Result
FlatMap
FlatMap
FlatMap
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Run the example
Suspend
GetLine
Return
s.toInt
Return
i + 1
Suspend
PrintLine
Result
FlatMap
FlatMap
FlatMap
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Run the example
Return
s.toInt
Return
i + 1
Suspend
PrintLine
Result
FlatMap
FlatMap
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Run the example
Return
i + 1
Suspend
PrintLine
Result
FlatMap
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
Run the example
Suspend
PrintLine
Result
PrintLine("Type a number")
.flatMap(_ => GetLine)
.map(s => s.toInt)
.map(i => i + 1)
.flatMap(r => PrintLine(r.toString))
How about forever?
def forever[B](a: IO[A]): IO[B] = a flatMap(_ => forever(a))
run(forever(PrintLine("Hello")))
Suspend
PrintLine
FlatMap
Now it works!
run(forever(PrintLine("Hello")))
Hello
Hello
…
<Keeps going!>
Takeaways
• Like a Free Monad! (A story for another day…)
• You don’t have to use the same interpreter... you can represent IO as ADTs
and not perform IO at all
• Effects are purposeful and partitioned off within a type
• Bonus – it happens to be a monad!
Other libraries
• Slick
• Describe SQL operations
• Execute them once they are prepared
• Akka Streams
• Describe a dataflow with a graph
• Push data through the graph components
• In Scala, this is an advanced flavor of the IO monad described here
Any Questions?
References
• Functional Programming in Scala, Chiusano and Bjarnason
• https://www.slideshare.net/InfoQ/purely-functional-io - Nice presentation
by Runar Bjarnason
• http://blog.higher-order.com/assets/scalaio.pdf

More Related Content

What's hot

C and C++ functions
C and C++ functionsC and C++ functions
C and C++ functions
kavitha muneeshwaran
 
Python Programming | JNTUA | UNIT 2 | Conditionals and Recursion |
Python Programming | JNTUA | UNIT 2 | Conditionals and Recursion | Python Programming | JNTUA | UNIT 2 | Conditionals and Recursion |
Python Programming | JNTUA | UNIT 2 | Conditionals and Recursion |
FabMinds
 
Functions in C++
Functions in C++Functions in C++
Functions in C++
Mohammed Sikander
 
Actors and functional_reactive_programming
Actors and functional_reactive_programmingActors and functional_reactive_programming
Actors and functional_reactive_programming
Diego Alonso
 
Learning C++ - Functions in C++ 3
Learning C++ - Functions  in C++ 3Learning C++ - Functions  in C++ 3
Learning C++ - Functions in C++ 3
Ali Aminian
 
Why functional programming and category theory strongly matters
Why functional programming and category theory strongly mattersWhy functional programming and category theory strongly matters
Why functional programming and category theory strongly matters
Piotr Paradziński
 
Intro to Functional Reactive Programming In Scala
Intro to Functional Reactive Programming In ScalaIntro to Functional Reactive Programming In Scala
Intro to Functional Reactive Programming In Scala
Diego Alonso
 
Operators in C++
Operators in C++Operators in C++
Operators in C++
Sachin Sharma
 
Functions in C++
Functions in C++Functions in C++
Functions in C++
Sachin Sharma
 
C++ Functions
C++ FunctionsC++ Functions
C++ Functions
sathish sak
 
16717 functions in C++
16717 functions in C++16717 functions in C++
16717 functions in C++
LPU
 
stack
stackstack
stack
Raj Sarode
 
Polish
PolishPolish
Polish
joie rocker
 
Category theory for beginners
Category theory for beginnersCategory theory for beginners
Category theory for beginners
kenbot
 
Lecture#6 functions in c++
Lecture#6 functions in c++Lecture#6 functions in c++
Lecture#6 functions in c++
NUST Stuff
 
Functions
FunctionsFunctions
Functions
Online
 
Introduction to c++
Introduction to c++Introduction to c++
Introduction to c++
Himanshu Kaushik
 

What's hot (17)

C and C++ functions
C and C++ functionsC and C++ functions
C and C++ functions
 
Python Programming | JNTUA | UNIT 2 | Conditionals and Recursion |
Python Programming | JNTUA | UNIT 2 | Conditionals and Recursion | Python Programming | JNTUA | UNIT 2 | Conditionals and Recursion |
Python Programming | JNTUA | UNIT 2 | Conditionals and Recursion |
 
Functions in C++
Functions in C++Functions in C++
Functions in C++
 
Actors and functional_reactive_programming
Actors and functional_reactive_programmingActors and functional_reactive_programming
Actors and functional_reactive_programming
 
Learning C++ - Functions in C++ 3
Learning C++ - Functions  in C++ 3Learning C++ - Functions  in C++ 3
Learning C++ - Functions in C++ 3
 
Why functional programming and category theory strongly matters
Why functional programming and category theory strongly mattersWhy functional programming and category theory strongly matters
Why functional programming and category theory strongly matters
 
Intro to Functional Reactive Programming In Scala
Intro to Functional Reactive Programming In ScalaIntro to Functional Reactive Programming In Scala
Intro to Functional Reactive Programming In Scala
 
Operators in C++
Operators in C++Operators in C++
Operators in C++
 
Functions in C++
Functions in C++Functions in C++
Functions in C++
 
C++ Functions
C++ FunctionsC++ Functions
C++ Functions
 
16717 functions in C++
16717 functions in C++16717 functions in C++
16717 functions in C++
 
stack
stackstack
stack
 
Polish
PolishPolish
Polish
 
Category theory for beginners
Category theory for beginnersCategory theory for beginners
Category theory for beginners
 
Lecture#6 functions in c++
Lecture#6 functions in c++Lecture#6 functions in c++
Lecture#6 functions in c++
 
Functions
FunctionsFunctions
Functions
 
Introduction to c++
Introduction to c++Introduction to c++
Introduction to c++
 

Viewers also liked

IO Resource Management on Exadata
IO Resource Management on ExadataIO Resource Management on Exadata
IO Resource Management on Exadata
Enkitec
 
EZ I/O Presentation at FCA
EZ I/O Presentation at FCAEZ I/O Presentation at FCA
EZ I/O Presentation at FCA
V. Bonales, M.D.
 
EZ-IO
EZ-IOEZ-IO
Intraosseous infusion
Intraosseous infusionIntraosseous infusion
Intraosseous infusion
Mohammad Javad Haji Ghanbari
 
Intraosseous Access and the Emergency Nurse
Intraosseous Access and the Emergency NurseIntraosseous Access and the Emergency Nurse
Intraosseous Access and the Emergency Nurse
Kane Guthrie
 
Async IO and Multithreading explained
Async IO and Multithreading explainedAsync IO and Multithreading explained
Async IO and Multithreading explained
Directi Group
 
Accessing I/O Devices
Accessing I/O DevicesAccessing I/O Devices
Accessing I/O Devices
Slideshare
 

Viewers also liked (7)

IO Resource Management on Exadata
IO Resource Management on ExadataIO Resource Management on Exadata
IO Resource Management on Exadata
 
EZ I/O Presentation at FCA
EZ I/O Presentation at FCAEZ I/O Presentation at FCA
EZ I/O Presentation at FCA
 
EZ-IO
EZ-IOEZ-IO
EZ-IO
 
Intraosseous infusion
Intraosseous infusionIntraosseous infusion
Intraosseous infusion
 
Intraosseous Access and the Emergency Nurse
Intraosseous Access and the Emergency NurseIntraosseous Access and the Emergency Nurse
Intraosseous Access and the Emergency Nurse
 
Async IO and Multithreading explained
Async IO and Multithreading explainedAsync IO and Multithreading explained
Async IO and Multithreading explained
 
Accessing I/O Devices
Accessing I/O DevicesAccessing I/O Devices
Accessing I/O Devices
 

Similar to Functional IO and Effects

Simple IO Monad in 'Functional Programming in Scala'
Simple IO Monad in 'Functional Programming in Scala'Simple IO Monad in 'Functional Programming in Scala'
Simple IO Monad in 'Functional Programming in Scala'
Philip Schwarz
 
What are monads?
What are monads?What are monads?
Algebraic Thinking for Evolution of Pure Functional Domain Models
Algebraic Thinking for Evolution of Pure Functional Domain ModelsAlgebraic Thinking for Evolution of Pure Functional Domain Models
Algebraic Thinking for Evolution of Pure Functional Domain Models
Debasish Ghosh
 
Functional Programming Patterns for the Pragmatic Programmer
Functional Programming Patterns for the Pragmatic ProgrammerFunctional Programming Patterns for the Pragmatic Programmer
Functional Programming Patterns for the Pragmatic Programmer
Raúl Raja Martínez
 
ZIO Queue
ZIO QueueZIO Queue
Functional programming with FSharp
Functional programming with FSharpFunctional programming with FSharp
Functional programming with FSharp
Daniele Pozzobon
 
pure-functional-programming.pdf
pure-functional-programming.pdfpure-functional-programming.pdf
pure-functional-programming.pdf
PuneetChaturvedi23
 
Drinking the free kool-aid
Drinking the free kool-aidDrinking the free kool-aid
Drinking the free kool-aid
David Hoyt
 
Scalapeno18 - Thinking Less with Scala
Scalapeno18 - Thinking Less with ScalaScalapeno18 - Thinking Less with Scala
Scalapeno18 - Thinking Less with Scala
Daniel Sebban
 
Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And Monad
Oliver Daff
 
Introducing Monads and State Monad at PSUG
Introducing Monads and State Monad at PSUGIntroducing Monads and State Monad at PSUG
Introducing Monads and State Monad at PSUG
David Galichet
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
John De Goes
 
A taste of Functional Programming
A taste of Functional ProgrammingA taste of Functional Programming
A taste of Functional Programming
Jordan Open Source Association
 
Functional Programming and Haskell - TWBR Away Day 2011
Functional Programming and Haskell - TWBR Away Day 2011Functional Programming and Haskell - TWBR Away Day 2011
Functional Programming and Haskell - TWBR Away Day 2011
Adriano Bonat
 
How to start functional programming (in Scala): Day1
How to start functional programming (in Scala): Day1How to start functional programming (in Scala): Day1
How to start functional programming (in Scala): Day1
Taisuke Oe
 
Talk - Query monad
Talk - Query monad Talk - Query monad
Talk - Query monad
Fabernovel
 
Advanced Tagless Final - Saying Farewell to Free
Advanced Tagless Final - Saying Farewell to FreeAdvanced Tagless Final - Saying Farewell to Free
Advanced Tagless Final - Saying Farewell to Free
Luka Jacobowitz
 
PyData NYC 2019
PyData NYC 2019PyData NYC 2019
PyData NYC 2019
Li Jin
 
Functional programming
Functional programmingFunctional programming
Functional programming
Prashant Kalkar
 
Functional Scala
Functional ScalaFunctional Scala
Functional Scala
Stan Lea
 

Similar to Functional IO and Effects (20)

Simple IO Monad in 'Functional Programming in Scala'
Simple IO Monad in 'Functional Programming in Scala'Simple IO Monad in 'Functional Programming in Scala'
Simple IO Monad in 'Functional Programming in Scala'
 
What are monads?
What are monads?What are monads?
What are monads?
 
Algebraic Thinking for Evolution of Pure Functional Domain Models
Algebraic Thinking for Evolution of Pure Functional Domain ModelsAlgebraic Thinking for Evolution of Pure Functional Domain Models
Algebraic Thinking for Evolution of Pure Functional Domain Models
 
Functional Programming Patterns for the Pragmatic Programmer
Functional Programming Patterns for the Pragmatic ProgrammerFunctional Programming Patterns for the Pragmatic Programmer
Functional Programming Patterns for the Pragmatic Programmer
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
Functional programming with FSharp
Functional programming with FSharpFunctional programming with FSharp
Functional programming with FSharp
 
pure-functional-programming.pdf
pure-functional-programming.pdfpure-functional-programming.pdf
pure-functional-programming.pdf
 
Drinking the free kool-aid
Drinking the free kool-aidDrinking the free kool-aid
Drinking the free kool-aid
 
Scalapeno18 - Thinking Less with Scala
Scalapeno18 - Thinking Less with ScalaScalapeno18 - Thinking Less with Scala
Scalapeno18 - Thinking Less with Scala
 
Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And Monad
 
Introducing Monads and State Monad at PSUG
Introducing Monads and State Monad at PSUGIntroducing Monads and State Monad at PSUG
Introducing Monads and State Monad at PSUG
 
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018Blazing Fast, Pure Effects without Monads — LambdaConf 2018
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
 
A taste of Functional Programming
A taste of Functional ProgrammingA taste of Functional Programming
A taste of Functional Programming
 
Functional Programming and Haskell - TWBR Away Day 2011
Functional Programming and Haskell - TWBR Away Day 2011Functional Programming and Haskell - TWBR Away Day 2011
Functional Programming and Haskell - TWBR Away Day 2011
 
How to start functional programming (in Scala): Day1
How to start functional programming (in Scala): Day1How to start functional programming (in Scala): Day1
How to start functional programming (in Scala): Day1
 
Talk - Query monad
Talk - Query monad Talk - Query monad
Talk - Query monad
 
Advanced Tagless Final - Saying Farewell to Free
Advanced Tagless Final - Saying Farewell to FreeAdvanced Tagless Final - Saying Farewell to Free
Advanced Tagless Final - Saying Farewell to Free
 
PyData NYC 2019
PyData NYC 2019PyData NYC 2019
PyData NYC 2019
 
Functional programming
Functional programmingFunctional programming
Functional programming
 
Functional Scala
Functional ScalaFunctional Scala
Functional Scala
 

Recently uploaded

Using Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional SafetyUsing Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional Safety
Ayan Halder
 
Oracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptxOracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptx
Remote DBA Services
 
What is Augmented Reality Image Tracking
What is Augmented Reality Image TrackingWhat is Augmented Reality Image Tracking
What is Augmented Reality Image Tracking
pavan998932
 
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, FactsALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
Green Software Development
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
rodomar2
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
Neo4j
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOMLORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
lorraineandreiamcidl
 
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdfRevolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Undress Baby
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptxLORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
lorraineandreiamcidl
 
Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
Aftab Hussain
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
Philip Schwarz
 
DDS-Security 1.2 - What's New? Stronger security for long-running systems
DDS-Security 1.2 - What's New? Stronger security for long-running systemsDDS-Security 1.2 - What's New? Stronger security for long-running systems
DDS-Security 1.2 - What's New? Stronger security for long-running systems
Gerardo Pardo-Castellote
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
TheSMSPoint
 
SMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API ServiceSMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API Service
Yara Milbes
 
Artificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension FunctionsArtificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension Functions
Octavian Nadolu
 
How to write a program in any programming language
How to write a program in any programming languageHow to write a program in any programming language
How to write a program in any programming language
Rakesh Kumar R
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
Hornet Dynamics
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Crescat
 
What is Master Data Management by PiLog Group
What is Master Data Management by PiLog GroupWhat is Master Data Management by PiLog Group
What is Master Data Management by PiLog Group
aymanquadri279
 

Recently uploaded (20)

Using Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional SafetyUsing Xen Hypervisor for Functional Safety
Using Xen Hypervisor for Functional Safety
 
Oracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptxOracle 23c New Features For DBAs and Developers.pptx
Oracle 23c New Features For DBAs and Developers.pptx
 
What is Augmented Reality Image Tracking
What is Augmented Reality Image TrackingWhat is Augmented Reality Image Tracking
What is Augmented Reality Image Tracking
 
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, FactsALGIT - Assembly Line for Green IT - Numbers, Data, Facts
ALGIT - Assembly Line for Green IT - Numbers, Data, Facts
 
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CDKuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
KuberTENes Birthday Bash Guadalajara - Introducción a Argo CD
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
 
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit ParisNeo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
Neo4j - Product Vision and Knowledge Graphs - GraphSummit Paris
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOMLORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
LORRAINE ANDREI_LEQUIGAN_HOW TO USE ZOOM
 
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdfRevolutionizing Visual Effects Mastering AI Face Swaps.pdf
Revolutionizing Visual Effects Mastering AI Face Swaps.pdf
 
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptxLORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
LORRAINE ANDREI_LEQUIGAN_HOW TO USE WHATSAPP.pptx
 
Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
 
Hand Rolled Applicative User Validation Code Kata
Hand Rolled Applicative User ValidationCode KataHand Rolled Applicative User ValidationCode Kata
Hand Rolled Applicative User Validation Code Kata
 
DDS-Security 1.2 - What's New? Stronger security for long-running systems
DDS-Security 1.2 - What's New? Stronger security for long-running systemsDDS-Security 1.2 - What's New? Stronger security for long-running systems
DDS-Security 1.2 - What's New? Stronger security for long-running systems
 
Transform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR SolutionsTransform Your Communication with Cloud-Based IVR Solutions
Transform Your Communication with Cloud-Based IVR Solutions
 
SMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API ServiceSMS API Integration in Saudi Arabia| Best SMS API Service
SMS API Integration in Saudi Arabia| Best SMS API Service
 
Artificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension FunctionsArtificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension Functions
 
How to write a program in any programming language
How to write a program in any programming languageHow to write a program in any programming language
How to write a program in any programming language
 
E-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet DynamicsE-commerce Development Services- Hornet Dynamics
E-commerce Development Services- Hornet Dynamics
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
 
What is Master Data Management by PiLog Group
What is Master Data Management by PiLog GroupWhat is Master Data Management by PiLog Group
What is Master Data Management by PiLog Group
 

Functional IO and Effects

  • 1. Functional I/O and Effects Or, if a Monad folds in a functional forest, does it make an effect? Dylan Forciea Oseberg February 28, 2017
  • 2. Topics • What does it mean for a language to be functional? • Creating a type to represent IO operations • Adding monoid operations to the IO type • Adding monad operations to the IO type • How a naïve approach for executing the monad is insufficient, and how trampolines can fix it
  • 3. What is a functional programming language? • Functions as first class objects? • Declarative rather than imperative? • Recursion rather than loops?
  • 4. ReferentialTransparency • Yes! • But ReferentialTransparency is also key to some advantages of functional programming • Any expression can be replaced with its value without changing the meaning of the program • This alludes to the fact that you should avoid side effects
  • 5. Example of ReferentialTransparency def average(x: Int, y: Int, z: Int): Int = (x + y + z) / 3 average(1, 2, 3)  (1 + 2 + 3) / 3  (6) / 3  2 Running this a second time with the same input values will always return the same result
  • 6. Counterexample of ReferentialTransparency var cumulativeValue: Int = 0 var numberSummed: Int = 0 def cumulativeAverage(x: Int): Int = { numberSummed = numberSummed + 1 cumulativeValue += x cumulativeValue / numberSummed } cumulativeAverage(10)  cumulativeValue = 10, numberSummed = 1, result = (10/1) = 10 cumulativeAverage(20)  cumulativeValue = 30, numberSummed = 2, result = (30/2) = 15 cumulativeAverage(20)  cumulativeValue = 50, numberSummed = 3, result = (50/3) = 16
  • 7. What does ReferentialTransparency buy you? • Testability • Promotes parallel programming • Can enable optimization of code
  • 8. ReferentialTransparency and IO def addOneToUserInput(): Int = scala.io.StdIn.readInt() + 1 The return value of executing this function will be different depending on what the user inputs!
  • 9. Wrapping IO in aType • Separate IO into a type and define actions inside • Denotes which pieces of the code are actions • Defer running an action until later • Describe a program containing effects purely functionally, and then run it
  • 10. Wrapping IO in aType trait IO[A] { def run: A } def PrintLine(msg: String): IO[Unit] = new IO[Unit] { def run: Unit = println(msg) } PrintLine(“Hello, world!”).run  Hello, world!
  • 11. How do we run a sequence of these actions? • Make it a monoid, of course! • What was a monoid, again?
  • 12. Monoid Refresher • A monoid is a combination of: • Some type, A • A seed (or "zero") for that type • An associative binary operator. • Concretely: trait Monoid[A] { def op(a1:A, a2: A): A def zero: A } • In addition to the associativity of op, for any A the following must hold: • op(a, zero) == a == op(zero, a)
  • 13. Monoid Refresher Example • For integer addition: object IntMonoid extends Monoid[Int] { def op(a: Int, b: Int): Int = a + b def zero: Int = 0 } • You can use operations like fold to combine these: • List(1, 2, 3).fold(IntMonoid.zero)(IntMonoid.op) • (((0 + 1) + 2) + 3) = 6
  • 14. IO as a Monoid trait IO[A] { self => def run:A def ++[B](io: IO[B]): IO[B] = new IO[B] { def run: B = { self.run; io.run } } } object IO { def zero: IO[Unit] = new IO[Unit] { def run: Unit = { } } def ++[A, B](io1: IO[A], io2: IO[B]): IO[B] = io1 ++ io2 }
  • 15. IO Monoid Example Usage (PrintLine("Hello!") ++ PrintLine("World!")).run Hello! World! val ioList = List(PrintLine("One"), PrintLine("Two"), PrintLine("Three")) ioList.fold(IO.zero)(IO.++).run One Two Three
  • 16. That’s great, but… • We can chain together output effects • But, how can we actually perform any operations on input effects? • Monads! • Wait, another one of those big words…
  • 17. Monad refresher • Monads provide two operations: • def map[B](f:A => B): IO[B] • def flatMap[B](f:A => IO[B]): IO[B]
  • 18. Monad refresher • map transforms a value from domainA to a value from domain B inside the monad • flatMap transforms a value from domain A into a Monad containing a value from domain B, and then returns a monad containing a value from domain B
  • 19. How does this help? • map takes the result of an IO monad and perform computations on the value • flatMap takes the result of an IO monad, and then perform IO for output • Like control flow for the IO “program”
  • 20. IO as a Monad trait IO[A] { self => def run:A def ++[B](io: IO[B]): IO[B] = new IO[B] { def run: B = { self.run; io.run } } def map[B](f: A => B): IO[B] = new IO[B] { def run: B = f(self.run) } def flatMap[B](f: A => IO[B]): IO[B] = new IO[B] { def run: B = f(self.run).run } } def PrintLine(msg: String): IO[Unit] = new IO[Unit] { def run: Unit = println(msg) } def GetLine(): IO[String] = new IO[String] { def run: String = scala.io.StdIn.readLine() }
  • 21. Lets put it together! PrintLine("Type a number") .flatMap(_ => GetLine()) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString)) .run Type a number  1 2
  • 22. Can’t mix and match GetLine().toInt + 1 <console>:15: error: value toInt is not a member of IO[String] GetLine().toInt + 1
  • 23. Some useful operations def doWhile[A](a: IO[A])(cond: A => IO[Boolean]): IO[Unit] = for { a1 <- a ok <- cond(a1) _ <- if (ok) doWhile(a)(cond) else IO.zero } yield () def forever[A, B](a: IO[A]): IO[B] = a flatMap(_ => forever(a))
  • 24. Houston, we have a problem! forever(PrintLine("Hello")).run Hello Hello … <Stack Overflow!> • What happened?!
  • 25. Not tail recursive def flatMap[B](f: A => IO[B]): IO[B] = new IO[B] { def run: B = f(self.run).run } def forever[B](a: IO[A]): IO[B] = a flatMap(_ => forever(a)) • forever keeps on calling flatMap • Every time flatMap is called, we end up one level lower in recursion • Since the function definition says we need to keep track of the results of f(this.run) to call run on it, we keep on adding on to the call stack every time
  • 26. How do we fix this? • Use Trampolining • Create a series ofAlgebraic DataTypes (ADTs) to describe how the operation will run • We can make running our IO monad operations tail recursive • First things first… • What is tail recursion? • What is anADT?
  • 27. Tail Recursion • Don’t want to keep track of state after calling function recursively • If the last function run is the recursive call, then it is in tail position • We can skip adding a stack frame
  • 28. Tail recursion example def factorial(n: BigInt): BigInt = if (n == 0) 1 else n * factorial(n-1) def factorial_tailrec(n: BigInt): BigInt = { def factorial1(n: BigInt, acc: BigInt): BigInt = if (n == 0) acc else factorial1(n - 1, n * acc) factorial1(n, 1) }
  • 29. Algebraic DataTypes • This is a data type that can be one of several things, and each thing can contain a defined set of data • We can use pattern matching to perform operations on the ADT • This is probably more apparent if we just give an example…
  • 30. List ADT definition sealed trait List[+A] case class Cons[+A](head: A, tail: List[A]) extends List[A] case object Nil extends List[Nothing]
  • 31. List ADT Pattern Matching val a = Cons(1, Nil) val b = Cons(2, a) val c = Cons(3, b) def add(list: List[Int]): Int = list match { case Cons(head, tail) => head + add(tail) case Nil => 0 } add(c) 6 3 2 1
  • 32. Describing IO monad operations with an ADT sealed trait IO[A] { self => def flatMap[B](f: A => IO[B]): IO[B] = FlatMap(self, f) def map[B](f: A => B): IO[B] = flatMap(a => (Return(f(a)))) } case class Return[A](a: A) extends IO[A] case class Suspend[A](resume: () =>A) extends IO[A] case class FlatMap[A, B](sub: IO[A], k: A => IO[B]) extends IO[B] def PrintLine(s: String): IO[Unit] = Suspend(() => Return(println(s))) def GetLine: IO[String] = Suspend(() => scala.io.StdIn.readLine())
  • 33. Our example from earlier! PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString)) Suspend PrintLine Prompt
  • 34. Our example from earlier! PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString)) Suspend PrintLine Prompt FlatMap Suspend GetLine
  • 35. Our example from earlier! PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString)) Suspend PrintLine Prompt FlatMap Suspend GetLine Return s.toInt FlatMap
  • 36. Our example from earlier! PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString)) Suspend PrintLine Prompt FlatMap Suspend GetLine Return s.toInt Return i + 1 FlatMap FlatMap
  • 37. Our example from earlier! PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString)) Suspend PrintLine Prompt FlatMap Suspend GetLine Return s.toInt Return i + 1 Suspend PrintLine Result FlatMap FlatMap FlatMap
  • 38. Now we need the Interpreter… def run[A](io: IO[A]): A = io match { case Return(a) => a case Suspend(r) => r() case FlatMap(x, f) => x match { case Return(a) => run(f(a)) case Suspend(r) => run(f(r())) case FlatMap(y, g) => run(y.flatMap(a => g(a).flatMap(f))) } }
  • 39. Run the example Suspend PrintLine Prompt FlatMap Suspend GetLine Return s.toInt Return i + 1 Suspend PrintLine Result FlatMap FlatMap FlatMap PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString))
  • 40. Run the example Suspend PrintLine Prompt FlatMap Suspend GetLine Return s.toInt Return i + 1 Suspend PrintLine Result FlatMap FlatMap FlatMap PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString))
  • 41. Run the example Suspend PrintLine Prompt FlatMap Suspend GetLine Return s.toInt Return i + 1 Suspend PrintLine Result FlatMap FlatMap FlatMap PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString))
  • 42. Run the example Suspend PrintLine Prompt FlatMap Suspend GetLine Return s.toInt Return i + 1 Suspend PrintLine Result FlatMap FlatMap FlatMap PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString))
  • 43. Run the example Suspend PrintLine Prompt FlatMap Suspend GetLine Return s.toInt Return i + 1 Suspend PrintLine Result FlatMap FlatMap FlatMap PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString))
  • 44. Run the example Suspend GetLine Return s.toInt Return i + 1 Suspend PrintLine Result FlatMap FlatMap FlatMap PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString))
  • 45. Run the example Return s.toInt Return i + 1 Suspend PrintLine Result FlatMap FlatMap PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString))
  • 46. Run the example Return i + 1 Suspend PrintLine Result FlatMap PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString))
  • 47. Run the example Suspend PrintLine Result PrintLine("Type a number") .flatMap(_ => GetLine) .map(s => s.toInt) .map(i => i + 1) .flatMap(r => PrintLine(r.toString))
  • 48. How about forever? def forever[B](a: IO[A]): IO[B] = a flatMap(_ => forever(a)) run(forever(PrintLine("Hello"))) Suspend PrintLine FlatMap
  • 50. Takeaways • Like a Free Monad! (A story for another day…) • You don’t have to use the same interpreter... you can represent IO as ADTs and not perform IO at all • Effects are purposeful and partitioned off within a type • Bonus – it happens to be a monad!
  • 51. Other libraries • Slick • Describe SQL operations • Execute them once they are prepared • Akka Streams • Describe a dataflow with a graph • Push data through the graph components • In Scala, this is an advanced flavor of the IO monad described here
  • 53. References • Functional Programming in Scala, Chiusano and Bjarnason • https://www.slideshare.net/InfoQ/purely-functional-io - Nice presentation by Runar Bjarnason • http://blog.higher-order.com/assets/scalaio.pdf

Editor's Notes

  1. Testability It is much easier to create test cases when the same inputs predictably result in the same output Promotes parallel programming When the results will be the same for a computation regardless of what is externally happening, parallelizing computations becomes almost trivial Can enable optimization of code For example, in Haskell, which is purely functional, execution order of functions in an expression is not defined and may not even be run at all (called laziness) This is safe to do because the compiler knows that running one function cannot change the result of running another function later if the inputs are the same
  2. We can separate IO into a type and define actions inside Allows us to denote which pieces of the code are actions We can also defer running an action until later Then, writing a program involves describing a program containing effects purely functionally, and then executing it
  3. map takes a value in domain A inside the type and performs a function to transform it to domain B Think of it like the Monad is a box that contains a value, and you are running a function to transform that value flatMap is like map, except the function returns another monad wrapping the result, and this is flattened down to one level Think of it like map, except you run a function that transforms that value into another box of the same type inside with a value inside it Then you take the item out of the inside box and make it the top level value
  4. map allows us to take the result of an IO monad as an input and perform computations on the value flatMap allows us to take the result of an IO monad as an input, and then perform IO for output Think of it like control flow for the IO “program” Pretty abstract, right? Let’s take a look at how it gets put together…
  5. We use a concept called Trampolining Rather than actually making the function call, we create a series of Algebraic Data Types (ADTs) to describe how the operation will run Using this, we can make running our IO monad operations tail recursive First things first… What is tail recursion? What is an ADT?
  6. We are trying to make it so when we call our function recursively, we don’t have to keep track of the result of the computation to combine it with another operation If the very last function call is the recursive call, then it is in tail position and we can skip adding a stack frame
  7. This approach is similar to a general concept called Free Monads, but that is a subject for another day… You don’t have to use the same interpreter... you can represent IO as ADTs and not perform IO at all Maybe just always return the same string Maybe write to a file rather than to the console instead Effects are purposeful and partitioned off within a type (which happens to be a monad!) These are not side effects! Describe the computation using an ADT purely functionally Execute the ADT with an intepreter