taking your side effects aside
TRANSCRIPT
![Page 1: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/1.jpg)
TAKING YOUR
SIDE-EFFECTS ASIDE
![Page 2: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/2.jpg)
About Me:
Software Engineer @
almendar
Scala since ~2011
Tomasz Kogut Warsaw
scala-poland
![Page 3: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/3.jpg)
Target Audience
3
![Page 4: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/4.jpg)
Spotting a side
effect
By brewing tea
![Page 5: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/5.jpg)
5
![Page 6: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/6.jpg)
6
![Page 7: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/7.jpg)
7
![Page 8: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/8.jpg)
8
![Page 9: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/9.jpg)
f( , , ) =
9
![Page 10: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/10.jpg)
f( , , ) =
10
![Page 11: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/11.jpg)
f( , , ) =
11
![Page 12: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/12.jpg)
f( , , ) =
12
![Page 13: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/13.jpg)
Kettle problems
13
![Page 14: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/14.jpg)
Kettle problems
14
![Page 15: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/15.jpg)
val deliciousTeaFut: Future[ ] =
Future[ ].map{kettle => (...) }
15
![Page 16: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/16.jpg)
val deliciousTeaFut: Future[ ] =
Future[ ].map{kettle => (...) }
(implicit ec:ExecutionContext)
16
![Page 17: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/17.jpg)
Data like / Static / Lazy
Service like / Dynamic / Eager
17
![Page 18: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/18.jpg)
Given impure A => B
it can be split into
pure A => C and
impure C => B with the needed side effect
18
![Page 19: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/19.jpg)
19
![Page 20: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/20.jpg)
f( , , ) = 20
![Page 21: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/21.jpg)
Types of Kettles
• Databases
• RPC endpoints (e.g. REST)
• Console (i.e. println, readLine)
• Logging
• Filesystem
• External devices
21
![Page 22: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/22.jpg)
Why not Future[ ]?
22
![Page 23: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/23.jpg)
Pure
Core
Side-effects
23
![Page 24: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/24.jpg)
Pure
Core
Side-effectsHere be
dragons!
24
![Page 25: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/25.jpg)
Side effects are not bad
Uncontrolled side effects are bad
25
![Page 26: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/26.jpg)
A => B
A => F[B]
26
![Page 27: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/27.jpg)
A => B
A => F[B]
27
![Page 28: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/28.jpg)
Capturing external
effects
A simple IO type
![Page 29: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/29.jpg)
trait IO { def run: Unit }
29
![Page 30: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/30.jpg)
trait IO { def run: Unit }
def PrintLine(msg: String): IO =
new IO { def run = println(msg) }
30
![Page 31: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/31.jpg)
trait IO { def run: Unit }
def PrintLine(msg: String): IO =
new IO { def run = println(msg) }
def printBigger(a: Int, b: Int): IO = {
if(a < b) PrintLine(a.toString)
else PrintLine(b.toString)
}
31
![Page 32: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/32.jpg)
trait IO { self =>
def run: Unit
def andThen(io: IO): IO = new IO {
def run = {self.run; io.run}
}
}
32
![Page 33: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/33.jpg)
trait IO { self =>
def run: Unit
def andThen(io: IO): IO = new IO {
def run = {self.run; io.run}
}
}
33
![Page 34: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/34.jpg)
trait IO { self =>
def run: Unit
def andThen(io: IO): IO = new IO {
def run = {self.run; io.run}
}
}
34
![Page 35: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/35.jpg)
@ val polite = PrintLine("Hello") andThen PrintLine("Goodbye")
polite: IO = $sess.cmd0$IO$$anon$1@43f88150
@ polite.run
Hello
Goodbye
@
35
![Page 36: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/36.jpg)
val cities = List("Warsaw", "Cracow", "Wroclaw")
val printCitiesList = cities.map(PrintLine)
val printAllCities: IO =
printCitiesList.foldLeft(IO.empty)(_ andThen _)
@ printAllCities.run
Warsaw
Cracow
Wroclaw
@
36
![Page 37: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/37.jpg)
“Code is data”
37
![Page 38: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/38.jpg)
What about Input?
38
![Page 39: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/39.jpg)
trait IO[A] { self =>
def run: A
}
39
![Page 40: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/40.jpg)
trait IO[A] { self =>
def run: A
def map[B](f: A => B): IO[B]
def flatMap[B](f: A => IO[B]): IO[B]
}
40
![Page 41: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/41.jpg)
trait IO[A] { self =>
def run: A
def map[B](f: A => B): IO[B] =
new IO[B] { def run = f(self.run) }
def flatMap[B](f: A => IO[B]): IO[B] =
new IO[B] { def run = f(self.run).run }
}
41
![Page 42: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/42.jpg)
trait IO[A] { self =>
def run: A
def map[B](f: A => B): IO[B] =
new IO[B] { def run = f(self.run) }
def flatMap[B](f: A => IO[B]): IO[B] =
new IO[B] { def run = f(self.run).run }
}
def ReadLine(): IO[String] = new IO[String] {
def run(): String = readLine
}42
![Page 43: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/43.jpg)
def ReadLine(): IO[String] = new IO[String] {
def run(): String = readLine}
def PrintLine(msg: String): IO[Unit] = new IO[Unit] {
def run = println(msg)
}
val enterNumberProgram =
for {
_ <- PrintLine("Enter a number:")
number <- ReadLine().map(_.toInt)
_ <- PrintLine(s"You entered $number")
} yield ()43
![Page 44: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/44.jpg)
val enterNumberProgram =
for {
_ <- PrintLine("Enter a number:")
number <- ReadLine().map(_.toInt)
_ <- PrintLine(s"You entered $number")
} yield ()
44
![Page 45: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/45.jpg)
val enterNumberProgram =
for {
_ <- PrintLine("Enter a number:")
number <- ReadLine().map(_.toInt)
_ <- PrintLine(s"You entered $number")
} yield ()
@ enterNumberProgram.run
Enter a number:
234
You entered 234
45
![Page 46: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/46.jpg)
val echoProgram = ReadLine.flatMap(PrintLine)
@ echoProgram.run
24
24
@
46
![Page 47: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/47.jpg)
Let’s break the IO
47
![Page 48: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/48.jpg)
def forever[A](io: IO[A]): IO[A] = {
lazy val t = forever(io)
io flatMap (_ => t)
}
48
![Page 49: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/49.jpg)
def forever[A](io: IO[A]): IO[A] = {
lazy val t = forever(io)
io flatMap (_ => t)
}
@ val greetLikeMad = forever(PrintLine("Hello"))
greetLikeMad: IO[Unit] = $sess.cmd0$IO$$anon$2@31e5554e
@ greetLikeMad.run
49
![Page 50: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/50.jpg)
def forever[A](io: IO[A]): IO[A] = {
lazy val t = forever(io)
io flatMap (_ => t)
}
@ val greetLikeMad = forever(PrintLine("Hello"))
greetLikeMad: IO[Unit] = $sess.cmd0$IO$$anon$2@31e5554e
@ greetLikeMad.run
Hello
Hello
(...)
Hello
java.lang.StackOverflowError
sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)
sun.nio.cs.UTF_8.access$200(UTF_8.java:57)
(...)
$sess.cmd0$IO$$anon$2.run(cmd0.sc:6)
$sess.cmd0$IO$$anon$2.run(cmd0.sc:6)
$sess.cmd0$IO$$anon$2.run(cmd0.sc:6) 50
![Page 51: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/51.jpg)
def flatMap[B](f: A => IO[B]): IO[B] =
new IO[B] { def run = f(self.run).run }
51
![Page 52: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/52.jpg)
“Code is data!!!”
52
![Page 53: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/53.jpg)
sealed trait IO[A] { self =>
def flatMap[B](f: A ⇒ IO[B]): IO[B] = ???
}
53
![Page 54: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/54.jpg)
sealed trait IO[A] { self =>
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
54
![Page 55: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/55.jpg)
sealed trait IO[A] { self =>
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
55
![Page 56: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/56.jpg)
sealed trait IO[A] { self =>
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
56
![Page 57: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/57.jpg)
sealed trait IO[A] { self =>
def map[B](f: A ⇒ B): IO[B] = ???
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
57
![Page 58: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/58.jpg)
sealed trait IO[A] { self =>
def map[B](f: A ⇒ B): IO[B] = ???
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
case class Return[A](a: A) extends IO[A]
58
![Page 59: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/59.jpg)
sealed trait IO[A] { self =>
def map[B](f: A ⇒ B): IO[B] = flatMap(f andThen (Return(_)))
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
case class Return[A](a: A) extends IO[A]
59
![Page 60: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/60.jpg)
sealed trait IO[A] { self =>
def map[B](f: A ⇒ B): IO[B] = flatMap(f andThen (Return(_)))
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
case class Return[A](a: A) extends IO[A]
def PrintLine(s: String): IO[Unit] = ???
60
![Page 61: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/61.jpg)
sealed trait IO[A] { self =>
def map[B](f: A ⇒ B): IO[B] = flatMap(f andThen (Return(_)))
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
case class Return[A](a: A) extends IO[A]
def PrintLine(s: String): IO[Unit] = Return(println(s))
61
![Page 62: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/62.jpg)
sealed trait IO[A] { self =>
def map[B](f: A ⇒ B): IO[B] = flatMap(f andThen (Return(_)))
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
case class Return[A](a: A) extends IO[A]
def PrintLine(s: String): IO[Unit] = Return(println(s))
62
![Page 63: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/63.jpg)
sealed trait IO[A] { self =>
def map[B](f: A ⇒ B): IO[B] = flatMap(f andThen (Return(_)))
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
case class Return[A](a: A) extends IO[A]
def PrintLine(s: String): IO[Unit] = ???
63
![Page 64: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/64.jpg)
sealed trait IO[A] { self =>
def map[B](f: A ⇒ B): IO[B] = flatMap(f andThen (Return(_)))
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
case class Return[A](a: A) extends IO[A]
case class Suspend[A](resume: () ⇒ A) extends IO[A]
def PrintLine(s: String): IO[Unit] = ???
64
![Page 65: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/65.jpg)
sealed trait IO[A] { self =>
def map[B](f: A ⇒ B): IO[B] = flatMap(f andThen (Return(_)))
def flatMap[B](f: A ⇒ IO[B]): IO[B] = FlatMap(this, f)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B]) extends IO[B]
case class Return[A](a: A) extends IO[A]
case class Suspend[A](resume: () ⇒ A) extends IO[A]
def PrintLine(s: String): IO[Unit] = Suspend(() => println(s))
65
![Page 66: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/66.jpg)
def PrintLine(s: String): IO[Unit] =
Suspend(() => Return(println(s)))
val p = IO.forever(PrintLine("...")) //this is flatMap
FlatMap(Suspend(() => println(s)), _ => FlatMap(Suspend(() =>
println(s)), _ => FlatMap(...)))
66
![Page 67: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/67.jpg)
def PrintLine(s: String): IO[Unit] =
Suspend(() => Return(println(s)))
val p = IO.forever(PrintLine("...")) //this is flatMap
FlatMap(Suspend(() => println(s)), _ => FlatMap(Suspend(() =>
println(s)), _ => FlatMap(...)))
IO[A]
67
![Page 68: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/68.jpg)
def PrintLine(s: String): IO[Unit] =
Suspend(() => Return(println(s)))
val p = IO.forever(PrintLine("...")) //this is flatMap
FlatMap(Suspend(() => println(s)), _ => FlatMap(Suspend(() =>
println(s)), _ => FlatMap(...)))
A => IO[B]
68
![Page 69: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/69.jpg)
“Code is data”
69
![Page 70: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/70.jpg)
“Code is data”
...but it has to run somewhere
70
![Page 71: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/71.jpg)
final def run[A](io: IO[A]): A
71
![Page 72: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/72.jpg)
final def run[A](io: IO[A]): A = io match {}
72
![Page 73: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/73.jpg)
final def run[A](io: IO[A]): A = io match {
case Return(x) ⇒ x
}
73
![Page 74: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/74.jpg)
final def run[A](io: IO[A]): A = io match {
case Return(x) ⇒ x
case Suspend(r) ⇒ r()
}
74
![Page 75: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/75.jpg)
final def run[A](io: IO[A]): A = io match {
case Return(x) ⇒ x
case Suspend(r) ⇒ r()
case FlatMap(y, g) ⇒
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B])
75
![Page 76: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/76.jpg)
final def run[A](io: IO[A]): A = io match {
case Return(x) ⇒ x
case Suspend(r) ⇒ r()
case FlatMap(y, g) ⇒ run(g(run(y)))
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B])
76
![Page 77: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/77.jpg)
@tailrec
final def run[A](io: IO[A]): A = io match {
case Return(x) ⇒ x
case Suspend(r) ⇒ r()
case FlatMap(y, g) ⇒ run(g(run(y)))
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B])
77
![Page 78: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/78.jpg)
@tailrec
final def run[A](io: IO[A]): A = io match {
case Return(x) ⇒ x
case Suspend(r) ⇒ r()
case FlatMap(y, g) ⇒
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B])
78
![Page 79: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/79.jpg)
@tailrec
final def run[A](io: IO[A]): A = io match {
case Return(x) ⇒ x
case Suspend(r) ⇒ r()
case FlatMap(y, g) ⇒ y match {
case Return(a1) ⇒
case Suspend(r) ⇒
case FlatMap(y1, g1) ⇒
}
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B])
79
![Page 80: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/80.jpg)
@tailrec
final def run[A](io: IO[A]): A = io match {
case Return(x) ⇒ x
case Suspend(r) ⇒ r()
case FlatMap(y, g) ⇒ y match {
case Return(a1) ⇒ run(g(a1))
case Suspend(r) ⇒ run(g(r()))
case FlatMap(y1, g1) ⇒
}
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B])
80
![Page 81: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/81.jpg)
@tailrec
final def run[A](io: IO[A]): A = io match {
case Return(x) ⇒ x
case Suspend(r) ⇒ r()
case FlatMap(y, g) ⇒ y match {
case Return(a1) ⇒ run(g(a1))
case Suspend(r) ⇒ run(g(r()))
case FlatMap(y1, g1) ⇒ run(y1 flatMap g1 flatMap g)
}
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B])
81
![Page 82: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/82.jpg)
@tailrec
final def run[A](io: IO[A]): A = io match {
case Return(x) ⇒ x
case Suspend(r) ⇒ r()
case FlatMap(y, g) ⇒ y match {
case Return(a1) ⇒ run(g(a1))
case Suspend(r) ⇒ run(g(r()))
case FlatMap(y1, g1) ⇒ run(y1 flatMap g1 flatMap g)
} //FlatMap(FlatMap(y1,g1), g)
}
case class FlatMap[A, B](sub: IO[A], k: A ⇒ IO[B])
82
![Page 83: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/83.jpg)
@tailrec
final def run[A](io: IO[A]): A = io match {
case Return(x) ⇒ x
case Suspend(r) ⇒ r()
case FlatMap(y, g) ⇒ y match {
case Return(a1) ⇒ run(g(a1))
case Suspend(r) ⇒ run(g(r()))
case FlatMap(y1, g1) ⇒
//run(y1 flatMap g1 flatMap g)
run(y1.flatMap(a ⇒ g1(a).flatMap(g)))
} // FlatMap(y1, a => FlatMap(g1(a), a1 => FlatMap...))
} 83
![Page 84: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/84.jpg)
84
Trampolining
• Trade stack for heap
• Build a call tree
• It has a higher cost than a function call
• It’s a special case of a Free Monad
• type IO[A] = Free[() => A, A]
![Page 85: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/85.jpg)
Let’s go Async
Getting dirty so you don’t have to
![Page 86: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/86.jpg)
“Code is data”
86
![Page 87: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/87.jpg)
case class Async[A](
register: ???
) extends IO[A]
87
![Page 88: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/88.jpg)
case class Async[A](
register: (A => Unit) => Unit
) extends IO[A]
88
![Page 89: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/89.jpg)
// def register[A](clb: A => Unit): Unit
case class Async[A](
register: (A => Unit) => Unit
) extends IO[A]
89
![Page 90: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/90.jpg)
import scala.concurrent.ExecutionContext.global
def DoubleOnSeperateThread(d: Double) = Async[Double] {
onDone =>
{
global.execute { () =>
onDone(d * 2.0) //(A => Unit)}
}
}
val program = ReadLine
.map(_.toDouble)
.flatMap(x => DoubleOnSeperateThread(x))
.flatMap(y => PrintLine(y.toString))
IO.run(program)
90
![Page 91: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/91.jpg)
@tailrec
final def run[A](io: IO[A]): A = io match {
(...)
case Async(register) => //(A => Unit) => Unit
val latch = new CountDownLatch(1)
var a: A = null.asInstanceOf[A]
register { a0 =>
a = a0
latch.countDown()
}
latch.await()
a 91
![Page 92: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/92.jpg)
@tailrec
final def run[A](io: IO[A]): A = io match {
(...)
case Async(register) => //(A => Unit) => Unit
val latch = new CountDownLatch(1)
var a: A = null.asInstanceOf[A]
register { a0 => //(A => Unit), onDone
a = a0
latch.countDown()
}
latch.await()
a 92
![Page 93: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/93.jpg)
Wrap up
• IO is usually called Task
• It can be used where you would have Future
• Open source implementations
• Monix
• FS2 (Functional Streams for Scala 2)
• Besides IO they also have streaming IO
93
![Page 94: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/94.jpg)
Further reading/watching
• “Stackless Scala With Free Monads”, Rúnar Bjarnason (pdf)
• “Functional Async on the JVM”, λC Winter Retreat 2017, Daniel
Spiewak (youtube)
• “Functional Programming in Scala”, Paul Chiusano, Rúnar
Bjarnason (chapter 13, book)
• IO Inside, https://wiki.haskell.org/IO_inside
• https://github.com/functional-streams-for-scala/fs2
• https://github.com/monix/monix
94
![Page 95: Taking your side effects aside](https://reader034.vdocument.in/reader034/viewer/2022042723/5a6649f97f8b9ae80e8b47e1/html5/thumbnails/95.jpg)
Thank you!
95almendar Tomasz Kogut