Reputation: 34081
I have the following code, that I would like to know, why the variable number
gets evaluated twice:
import cats.effect.IO
import scala.util.Random
object Main {
private val number: IO[Int] =
IO(Random.between(3, 300))
def main(args: Array[String]): Unit = {
number
.flatMap { e =>
println(e);
number
}.flatMap { e =>
println(e);
IO.unit
}.unsafeRunSync()
}
}
the program prints two different numbers, although the number is an assignment. I know here, I describe a computation not a execution, and at the end of the universe, I run the program.
The question is, why it prints out two different numbers?
Upvotes: 2
Views: 109
Reputation: 20843
There is a difference between
private val number: IO[Int] = IO(Random.nextInt())
and
private val number2: Int = Random.nextInt()
number
is a value that when evaluated computes a random number. When evaluated multiple times this value of type IO
(aka this computation) is run multiple times resulting in multiple different random numbers.
Whereas number2
when evaluated is just a single number.
It is very similar to the distinction between a lambda (val lambda = () => Random.nextInt()
) and a value (val value = Random.nextInt()
).
Upvotes: 5
Reputation: 48420
IO
is a bit similar to the following scenario
final case class SuspendedComputation[T](f: () => T) {
def run: T = f()
}
val v = SuspendedComputation(Random.nextInt)
v.run
v.run
which outputs something like
v: SuspendedComputation[Int] = SuspendedComputation(<function>
res2: Int = -1062309211
res3: Int = 765640585
Note how SuspendedComputation
internally stores computation as () => Random.nextInt
and then uses run
method to actually evaluate computation f
.
Similarly, IO.apply
accepts an argument by-name : => A
and eventually constructs Delay
object that stores the un-evaluated computation in a field as () => A
and then uses unsafeRunSync
to actually evaluate computation.
Upvotes: 2