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 aloneprintln
insideIO.pure()
- io.unsafeRunSync() will print
"lazy"
first then"pure"
, becausef
in flatMap() has to wait until the first IO has an evaluated value. (You need to read cats-effect code for the details)