It’s said IO.pure() means eager evaluation. But it doesn’t seem to so in this example:
object TryPure1 extends App {
def printTwice(): IO[Unit] =
IO.apply(println("print in lazy")).flatMap(_ => IO.pure(println("print in pure")))
val io = printTwice() //no output
io.unsafeRunSync() //output:
//print in lazy
//print in pure
}
Here println("print in pure") is executed just like as if it’s IO.apply()
Another example, however, discloses the truth
object TryPure2 extends App{
def printTwice(): IO[Unit] =
IO.apply(println("print in lazy")) *> IO.pure(println("print in pure"))
printTwice() //output: print in pure
}
Here is it how it works:
- When
printTwice()is called, every expression inside it is evaluated IO.apply(println("print in lazy"))is evaluated as an IO, without theprintln()executed because this IO is an lazy IO- The laziness is implemented by “using a by-name parameter as a property of a case class”
IO.pure(println("print in pure"))is also evaluated as an IO, and theprintln()inside it is executed right away, because this IO is an eager IO- If you then run
unsafeRunSync()on this combined IO, then you will see"print in lazy"because now expressions in lazy IOs are evaluated- There won’t be another
"print in pure"because of memory – the value has been evaluated when callingprintTwice()
- There won’t be another
So when is xxx inside IO.pure(xxx) executed? It’s executed when IO is constructed using IO.pure() , and it’s before this IO really runs
Now go back to the first code example. Why is the println inside IO.pure() not executed ?
The answer is IO.flatMap() is also lazy (“using a by-name parameter as a property of a case class”, if you read cats-effect code) . So,
- Calling
printTwice()will not lead to the running ofIO.pure(), let aloneprintlninsideIO.pure() - io.unsafeRunSync() will print
"lazy"first then"pure", becausefin flatMap() has to wait until the first IO has an evaluated value. (You need to read cats-effect code for the details)