-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Closed
Description
I encountered odd behaviour when using Eval as Applicative to traverse when doing stack safe serialization / deserialization.
I managed to see that these syntaxes behave differently.
.traverse(_ => f).as(())
.traverse(_ => f)
.traverse(_ => f).void
.traverse_(_ => f)I would expect all these execute f in exactly the same way, but it's not. Fo IO it works as expected.
Here is the code https://scastie.scala-lang.org/nzpr/UF5cug4wSYSeFurF434r9w/1
import cats.effect.IO
import cats.effect.unsafe.implicits.global
import cats.syntax.all._
import cats.{Applicative, Eval}
val traversable: List[Int] = (0 until 10).toList
// 4 ways to traverse list
// this works for Eval, use .as to make sure Unit is return type
def ok1[F[_]: Applicative](f: F[Unit]): F[Unit] = traversable.traverse(_ => f).as(())
// this works for Eval, make return type List[Unit]
def ok2[F[_]: Applicative](f: F[Unit]): F[List[Unit]] = traversable.traverse(_ => f)
// this does not work for Eval, use .void to make sure Unit is return type
def err1[F[_]: Applicative](f: F[Unit]): F[Unit] = traversable.traverse(_ => f).void
// this does not work for Eval, use .traverse_ to make sure Unit is return type
def err2[F[_]: Applicative](f: F[Unit]): F[Unit] = traversable.traverse_(_ => f)
// Mutable var to count how many times effect is executed.
var x = 0
// The effect. Calling these should increase x by 1.
def fEval: Eval[Unit] = Eval.always(x += 1) // Eval effect, this is what misbehaves
def fIO: IO[Unit] = IO.delay(x += 1) // IO effect (for comparison)
// Traverse using Eval
x = 0
ok1(fEval).value
val eval1 = x
x = 0
ok2(fEval).value
val eval2 = x
x = 0
err1(fEval).value
val eval3 = x
x = 0
err2(fEval).value
val eval4 = x
// Traverse using IO (for comparison)
x = 0
ok1(fIO).unsafeRunSync()
val io1 = x
x = 0
ok2(fIO).unsafeRunSync()
val io2 = x
x = 0
err1(fIO).unsafeRunSync()
val io3 = x
x = 0
err2(fIO).unsafeRunSync()
val io4 = x
println(List(eval1, eval2, eval3, eval4))
// List(10, 10, 0, 0)
println(List(io1, io2, io3, io4))
// List(10, 10, 10, 10)
// So with Eval effects are not executed for .traverse(_ => f).void and .traverse_(_ => f)Metadata
Metadata
Assignees
Labels
No labels