A ZIO cheatsheet

As a very brief introduction, this is the start of a ZIO cheat sheet. If you want a good cheat sheet, see this one on github. I’m creating my own as I learn ZIO and read the Zionomicon book. During the learning process I find that it’s much better to create your own by hand, that way you get something that’s meaningful to you.

Note that almost all of these initial examples come from the ZIOnomicon book and the video that I link to later.

ZIO build.sbt configuration

Use the latest stable version:

libraryDependencies += "dev.zio" %% "zio" % "1.0.5"

See the ZIO Getting Started page for more info. It also shows zio.App and zio.console. examples.

How to create a ZIO.App

How to create and run a ZIO.App:

import zio._
object GroceryStore extends App {
    def run(args: List[String]) = goShopping.exitCode
}

// exitCode turns all successes into ExitCode(0) and 
// failures into ExitCode(1)

Sequential composition/computation

Methods like flatMap and zip* let you get back to sequential computation, just like procedural programming, i.e., doing one thing after another:

import scala.io.StdIn
val readLine = ZIO.effect(StdIn.readLine())
def printLine(line: String) = ZIO.effect(println(line))

// execute readLine, then pass its result to printLine.
// flatMap can be read like “and then do Expression2 with
// the result of Expression1”:
val echo = readLine.flatMap(line => printLine(line))
  • you can chain a bunch of flatMap’s together like this
  • but it’s easier to read a for-expression/comprehension
  • that last line of code is equivalent to this:
import zio._
val echo = for {
    line <- readLine
    _    <- printLine(line)
} yield ()

Other sequential operators

Operator Description
zipWith combine two effects sequentially; merge their two results with an anonymous function (or similar)
zip combine two effects sequentially, yield a tuple
zipLeft combine two effects sequentially, return the first’s result
zipRight combine two effects sequentially, return the second’s result
foreach return a single effect that describes the process of performing an effect for each element of a sequence
collectAll return “a single effect that collects the results of a whole collection of effects”
  • zipWith syntax:
val firstName = ZIO.effect(StdIn.readLine("..."))
val lastName = ZIO.effect(StdIn.readLine("..."))
val fullName = firstName.zipWith(lastName)((first, last) => s"$first $last")

How it works:

val fullName = firstName.zipWith(lastName)((first, last) => s"$first $last")
                  ^                 ^                   ^
               effect #1        effect #2     merge them w/ this function

Aliases

Method Alias
zipLeft <*
zipRight *>
  • Example:

Combine two effects sequentially and return the Unit success value of the right-hand effect:

val helloWorld =
    ZIO.effect(print("Hello, ")) *> ZIO.effect(print("World!\n"))

ZIO type parameters

Type Description
R The environment
E The possible error result type
A The possible (hopeful) success result type

See the ZIO Overview page for more details.

ZIO type aliases

Type aliases you can use instead of the full ZIO type signature:

Alias Full Type Signature
IO [E, A] ZIO[Any, E, A]
Task[A] ZIO[Any, Throwable, A]
RIO [R, A] ZIO[R, Throwable, A]
UIO [A] ZIO[Any, Nothing, A]
URIO[R, A] ZIO[R, Nothing, A]

See the ZIO Overview page for more details.

delay, run later, timer/clock

// ZIOnomicon example
import zio.clock._
import zio.duration._

val goShoppingLater = goShopping.delay(1.hour)

A Tour of ZIO (video)

These are my notes from watching the A Tour of ZIO video. They’re not very organized, but hopefully I’ll fix them up one day.

ZIO is based on fiber-based concurrency:

  • fibers are coming to the JVM via Project Loom
  • until then, you can use libraries like ZIO to get fibers now
  • ZIO is 100% async
    • even code that looks like it’s blocking isn’t blocking
  • uses type system to catch bugs at compile time when they’re easiest and cheapest to fix

Most important data type is called ZIO:

  • ZIO[-R, +E, +A]
    • can be thought of as a functional effect, or just effect
    • can be thought of as an immutable value
    • represents a job or workflow or task
    • can think of an effect as being a lazy description of various types of interactions with the outside world
      • printing text, databases, networks, etc.
    • ZIO effects are 100% lazy
    • nothing has happened; have to run “execution” to translate your description of the interaction with the outside world into actual interaction; that’s called running or executing the effect
    • do that with one of the “unsafe” method
    • R represents an environment you’re passing in

Mental model:

  • ZIO[R,E,A]
    • a function of the R parameter to an Either[E, A]
      • to either an E or an A
      • function takes an R
      • E is a failure type, A is a success type
      • you give the functions an R and get an E or an A
        • always get a failure or a success
    • an effect can be thought of as requiring some environment type R, which can be a database connection or some configuration or an http connection or spark client

ZIO has five type aliases that are common simplifications of ZIO[R,E,A]:

  • Five type aliases:
    • Task[+A] = ZIO[Any, Throwable, A]
      • an effect that doesn’t need anything
      • Any lets you model an effect that doesn’t need anything
    • UIO[+A] = ZIO[Any, Nothing, A]
      • doesn’t need anything
      • cannot fail (Nothing)
      • Nothing means the effect can’t fail
      • Nothing is a special type, there are no values of it
    • RIO[-R, +A] = ZIO[R, Throwable, A]
    • IO[+E, +A] = ZIO[Any, E, A]
      • does not need anything
      • fails with E or succeeds with A
    • URIO[-R,+A] = ZIO[R, Nothing, A]

zio.App:

  • zio.App
  • App expects you to override run
  • it also bakes in a runtime that executes the effect that is returned from run
  • run:
    • def run(args: List[String]): ZIO[ZEnv, Nothing, Int] = ...

HelloWorld:

  • have to return an effect from run
  • putStrLn prints a line of text and returns an effect
// an interactive application
object PromptName extends App {
    import zio.console._
    def run(args: List[String]): ZIO[ZEnv, Nothing, Int] = 
        putStrLn("What is your name? ") *>
        // can’t use zipRight operator here; if you want to do several
        // things in sequence, and what you’re working on depends on
        // the return/success values of what came before, you need to 
        // use flatMap, which is sometimes known as `chain` or `andThen`
        // in other languages
        getStrLn.flatMap(name => putStrLn(s"Hello, $name")).fold(
            _ => 1,
            _ => 0
        )
        
        // more readable:
        (for {
            // use _ because you don’t care about the Unit value
            _    <- putStrLn("What is your name? ")  // flatMap
            name <- getStrLn                         // flatMap
            _    <- putStrLn(s"Hello, $name")        // map
        } yield 0) orElse ZIO.succeed(1)
}

Resources