October 22, 2020
War
Greed
Envy
Famine
Death
Poverty
I/O
Side Effects
Errors
Concurrency
Resource Management
Asynchronous
Hope
Hope?
What is the analog of Pandoran Hope?
¯\_(ツ)_/¯
Change the rules so that success is possible.
module Main (main) where
main :: IO ()
main = putStrLn "Hello, World!"ZIO[R, E, A]Succeeds with a value of type A
Fails with a value of type E
Requires an environment of type R
import zio._
import console._
object Main extends App {
override def run(args: List[String]) =
putStrLn("Hello, world!").exitCode
}
import zio._
import console._
object Main extends App {
override def run(args: List[String]): ZIO[Console, Nothing, ExitCode] =
putStrLn("Hello, world!").exitCode
}
// console.putStrLn(line: => String): ZIO[Console, Nothing, Unit]object Prompt extends App {
val program: ZIO[Console, IOException, Unit] =
for {
_ <- putStr("What's your name?: ")
n <- getStrLn
_ <- putStrLn(s"Hello, $n!")
} yield ()
override def run(args: List[String]): ZIO[Console, Nothing, ExitCode] =
program.exitCode
}
// console.getStrLn: ZIO[Console, IOException, String]getStrLn can fail with IOException
exitCode transforms ZIO[R, E, A] to ZIO[R, Nothing, ExitCode]
object Prompt2 extends App {
val program: ZIO[Console, IOException, Unit] =
for {
_ <- putStr("What's your name?: ")
n <- getStrLn
_ <- putStrLn(s"Hello, $n!")
} yield ()
override def run(args: List[String]): ZIO[Console, Nothing, ExitCode] =
program
.catchAll { e => putStrLn("") *> putStrLn(s"Input failed due to '${e.getMessage}'") }
.exitCode
}
// catchAll[R1 <: R, E2, A1 >: A](h: (E) => ZIO[R1, E2, A1]): ZIO[R1, E2, A1]Recover from all errors
object PromptRepeat extends App {
val program: ZIO[Console, IOException, Unit] =
for {
_ <- putStr("What's your name?: ")
n <- getStrLn
_ <- putStrLn(s"Hello, $n!")
} yield ()
override def run(args: List[String]): ZIO[Console, Nothing, ExitCode] =
program
.forever // ZIO[R, E, A] => ZIO[R, E, Nothing]
.catchSome { case _: EOFException => putStrLn("") *> putStrLn("Bye")}
.exitCode
}val a: ZIO[Any, Throwable, Int] = ZIO.fail(new IOException("Bang!"))
a.orElse { ZIO.succeed(42) }
a.mapError { _.getMessage }
a.either // ZIO[R, E, A] => ZIO[R, Nothing, Either[E, A]]
a.eventually // Repeat until successMore ways to handle errors
val a: ZIO[Console, Nothing, String] = putStrLn("A").as("A")
val b: ZIO[Console, Nothing, Int] = putStrLn("1").as(1)
val f: ZIO[Any, String, Nothing] = ZIO.fail("Bang!")
val aZipB: ZIO[Console, Nothing, (String, Int)] = a <*> b
val aZipBLeft: ZIO[Console, Nothing, String] = a <* b
val aZipBRight: ZIO[Console, Nothing, Int] = a *> b
val aZipFail: ZIO[Console, String, (String, Nothing)] = a <*> f
val failZipB: ZIO[Console, String, (Nothing, Int)] = f <*> bZip two ZIOs into a single ZIO
val a: ZIO[Console, Nothing, String] = putStrLn("A").as("A")
val b: ZIO[Console, Nothing, Int] = putStrLn("1").as(1)
val f: ZIO[Any, String, Nothing] = ZIO.fail("Bang!")
val aParB: ZIO[Console, Nothing, (String, Int)] = a <&> b
val aParBLeft: ZIO[Console, Nothing, String] = a <& b
val aParBRight: ZIO[Console, Nothing, Int] = a &> b
val aParFail: ZIO[Console, String, (String, Nothing)] = a <&> f
val failParB: ZIO[Console, String, (Nothing, Int)] = f <&> bZip two ZIOs in parallel
val a: ZIO[Console, Nothing, String] = putStrLn("A").as("A")
val b: ZIO[Console, Nothing, String] = putStrLn("B").as("B")
val c: ZIO[Console, Nothing, String] = putStrLn("C").as("C")
val f: ZIO[Any, String, Nothing] = ZIO.fail("Bang!")
val x: URIO[Console, String] = a.race(b)
val y: URIO[Console, String] = a.raceAll(List(b, c, f))
val z: URIO[Console, Either[String, String] = a.raceEither(b)Race ZIOs returning the first to succeed
val a: Task[BufferedSource] =
ZIO.effect {
Source.fromFile("/tmp/fubar")
}
// type Task[A] = ZIO[Nothing, Throwable, A]
// ZIO.effect[A](effect: => A): Task[A]Create a ZIO from Scala code with side effects
val a: UIO[Unit] =
ZIO.effectTotal {
logger.info("Hello, world!")
}
// type UIO[A] = ZIO[Nothing, Nothing, A]
// ZIO.effectTotal[A](effect: => A): UIO[A]
Create a ZIO from Scala code that can't fail
val a: IO[String, String] =
ZIO.fromEither {
basicRequest
.get(uri"http://iscaliforniaonfire.com").send()
.body
}
// type IO[E, A] = ZIO[Nothing, E, A]
// ZIO.fromEither[E, A](v: => Either[E, A]): IO[E, A]Create a ZIO from an Either
val a: RIO[Blocking, ConsumerRecords] =
blocking.effectBlockingCancelable {
consumer.poll(60.seconds)
} { consumer.wakeup() }
// type RIO[R, A] = ZIO[R, Throwable, A]
// effectBlockingCancelable(effect: => A)
// (cancel: UIO[Unit]): RIO[Blocking, A]Create a ZIO for a blocking effect
def repeatN(n: Int): ZIO[R, E, A]
def repeatUntil(f: (A) ⇒ Boolean): ZIO[R, E, A]
def repeatUntilM[R1 <: R](f: (A) ⇒ URIO[R1, Boolean]): ZIO[R1, E, A]
def repeatWhile(f: (A) ⇒ Boolean): ZIO[R, E, A]
def repeatWhileM[R1 <: R](f: (A) ⇒ URIO[R1, Boolean]): ZIO[R1, E, A]Repeating a ZIO
zio.forever: ZIO[R, E, Nothing]Repeat an io continuously until failure
Repeat an io continuously until success
zio.eventually: ZIO[R, Nothing, A]zio.repeat[R1 <: R, B](schedule: Schedule[R1, A, B]): ZIO[R1 with Clock, E, B]Repeat an io on a schedule or until failure
Repeat an io on a schedule or until success
zio.retry[R1 <: R, S](policy: Schedule[R1, E, S]): ZIO[R1 with Clock, E, A]A Schedule[R, I, O] represents a sequence of intervals
At each interval the schedule consumes an I, determines whether or not to recur and if so produces a value of type O
once: Schedule[Any, Any, Unit]
forever: Schedule[Any, Any, Long]
recurs(n: Int): Schedule[Any, Any, Long]
recurUntil[A](f: (A) => Boolean): Schedule[Any, A, A]
recurWhile[A](f: (A) => Boolean): Schedule[Any, A, A]
fixed(interval: Duration): Schedule[Any, Any, Long]
spaced(duration: Duration): Schedule[Any, Any, Long]
exponential(base: Duration, factor: Double=2.0): Schedule[Any, Any, Duration]
linear(base: Duration): Schedule[Any, Any, Duration]Primative Schedules
Schedule Combinators
Schedule.fixed(60.seconds) && Schedule.recurs(10)Fixed interval for 10 repetitions
Schedule.exponential(500.millis) || Schedule.fixed(1.minute)Exponential backoff while less than 1 minute then fixed 1 minute interval
(Schedule.exponential(500.millis) || Schedule.fixed(1.minute)).jittered
&& Schedule.recurs(100)Jittered exponential backoff while less than 1 minute then fixed interval for 100 total repetitions
(Schedule.spaced(1.second) && Schedule.recurs(5)) andThen
(Schedule.spaced(5.seconds) && Schedule.recurs(5))5 intervals with 1 second spacing then 5 intervals with 5 second spacing
val program =
for {
status <- statusReporter.fork
http <- httpServer.fork
data <- dataLoop.fork
_ <- Fiber.joinAll(List(status, http, data))
} yield ()Create fibers with fork
val dataLoop: ZIO[Blocking with Clock, Throwable, Long] =
ZIO.bracket(DataSource())(_.close().ignore) { source =>
(for {
ds <- source.fetch(30.seconds)
ws <- mkWidgets(ds)
_ <- (publishWidgets(ws) <* source.commit()).uninterruptible
} yield ()).repeat(Schedule.forever)
}Manage resources with Bracket
import zio._
object module {
type Module = Has[Module.Service]
object Module {
trait Service {
def doSomething(i: Int): ZIO[Nothing, IOException, String]
}
val live: ZLayer[Any, Nothing, Module] =
new ZLayer.succeed(
new Module.Service {
def doSomething(i: Int): ZIO[Nothing, IOException, String] = ???
}
)
}
def doSomething(i: Int): ZIO[Module, IOException, String] =
ZIO.accessM(_.get.doSomething(i))
}Modules
val env = Module1.live +++ Console.live +++ (Module2.live >>> Module3.live)
program
.provideLayer(env)
.exitCodeConstruct an environment using layer combinators
TArray[A]
TMap[K, V]
TPriorityQueue[A]
TPromise[E, A]
TQueue[A]
TSemaphore
TSet[A]Software Transactional Memory
High Level Concurrency Tools
ZRef
ZRef.set(a: A): IO[E, Unit]
ZRef.get: IO[E, A]Demo?