Basic Operations
Mapping
You can map over the success channel of an effect by calling the ZIO#map
method. This lets you transform the success values of effects.
import zio._
val succeeded: UIO[Int] = IO.succeed(21).map(_ * 2)
You can map over the error channel of an effect by calling the ZIO#mapError
method. This lets you transform the failure values of effects.
val failed: IO[Exception, Unit] =
IO.fail("No no!").mapError(msg => new Exception(msg))
Note that mapping over an effect's success or error channel does not change the success or failure of the effect, in the same way that mapping over an Either
does not change whether the Either
is Left
or Right
.
Chaining
You can execute two effects in sequence with the flatMap
method, which requires that you pass a callback, which will receive the value of the first effect, and can return a second effect that depends on this value:
val sequenced =
getStrLn.flatMap(input => putStrLn(s"You entered: $input"))
If the first effect fails, the callback passed to flatMap
will never be invoked, and the composed effect returned by flatMap
will also fail.
In any chain of effects, the first failure will short-circuit the whole chain, just like throwing an exception will prematurely exit a sequence of statements.
For Comprehensions
Because the ZIO
data type supports both flatMap
and map
, you can use Scala's for comprehensions to build sequential effects:
val program =
for {
_ <- putStrLn("Hello! What is your name?")
name <- getStrLn
_ <- putStrLn(s"Hello, ${name}, welcome to ZIO!")
} yield ()
For comprehensions provide a more procedural syntax for composing chains of effects.
Zipping
You can combine two effects into a single effect with the ZIO#zip
method. The resulting effect succeeds with a tuple that contains the success values of both effects:
val zipped: UIO[(String, Int)] =
ZIO.succeed("4").zip(ZIO.succeed(2))
Note that zip
operates sequentially: the effect on the left side is executed before the effect on the right side.
In any zip
operation, if either the left or right hand sides fail, then the composed effect will fail, because both values are required to construct the tuple.
Sometimes, when the success value of an effect is not useful (for example, it is Unit
), it can be more convenient to use the ZIO#zipLeft
or ZIO#zipRight
functions, which first perform a zip
, and then map over the tuple to discard one side or the other:
val zipRight1 =
putStrLn("What is your name?").zipRight(getStrLn)
The zipRight
and zipLeft
functions have symbolic aliases, known as *>
and <*
, respectively. Some developers find these operators easier to read:
val zipRight2 =
putStrLn("What is your name?") *>
getStrLn
Next Step
If you are comfortable with the basic operations on ZIO effects, then the next step is to learn about error handling.