I just wrote the following ZIO 2 question about how to use ZIO.cond
to a friend, and got the answer shown. I’ve also added in my own comments where they make sense.
ZIO.done question
Hey, I’m trying to understand why my ZIO failWithMsgEffect
doesn’t seem to get run in the following code example?
I have learned that there are better ways to handle this, but I’ve found that if I don’t understand something like this, it will come back to bite me later. Here’s the code:
//> using scala "3"
//> using lib "dev.zio::zio::2.1.0"
package zio_done
import zio.*
import zio.Console.*
object ZioMinimalDoneTest extends ZIOAppDefault:
// NOTE: this never seems to get run
val failWithMsgEffect =
printLineError("Usage: yada yada...").flatMap { _ =>
ZIO.fail(new Exception("Usage error"))
}
val blueprint =
for
args <- ZIOAppArgs.getArgs
rez <- ZIO.cond(args.size >= 1, (), failWithMsgEffect)
_ <- printLine(s"\nfor is still running\n")
yield
args(0)
def run = blueprint.foldZIO(
failure => printLineError(s"FAILURE = $failure"),
success => printLine( s"SUCCESS = $success")
)
If I run that WITH a command-line argument, I see the "for is still running"
output and get the "SUCCESS"
message, but if I run it WITHOUT any command-line arguments all I see is this output:
FAILURE = FlatMap(zio_done.ZioMinimalDoneTest.failWithMsgEffect(ZioDoneTestMinimal.scala:13),Stateful(zio_done.ZioMinimalDoneTest.failWithMsgEffect(ZioDoneTestMinimal.scala:11),zio.FiberRef$unsafe$$anon$2$$Lambda$43/0x000000030017ae10@2d285785),zio_done.ZioMinimalDoneTest$$$Lambda$44/0x000000030017be48@40aa7f74)
I don’t see any of the strings in failWithMsgEffect
. I’ve tried implementing failWithMsgEffect
with a for
expression and with *>
, but as far as I can tell, it never gets invoked. What am I doing wrong?
Solution
The problem you’re running into is due to the behavior of ZIO.cond
. The way it works is that it WILL NOT execute the effect in the failure case — which is failWithMsgEffect
in your code — if the condition fails. Instead, it directly fails with the effect itself as a failure, not its result. So basically failWithMsgEffect
is not being run, as you have noted, but instead, it’s being passed as a value to indicate failure.
To make it clearer, ZIO.cond
works like this:
- If the condition is
true
, it produces the success value. - If the condition is
false
, it produces the failure value. (It fails with the given effect as the failure content/value, but does not execute that effect.)
To get what you want, which is to execute failWithMsgEffect
when the condition is false
, you need to explicitly run the effect when the condition fails. You do this by using more explicit conditional handling rather than ZIO.cond
.
For instance, you might be able to use flatMap
, or in this case an if
condition will due the trick:
val blueprint =
for
args <- ZIOAppArgs.getArgs
rez <- if args.size >= 1 then ZIO.succeed(()) else failWithMsgEffect
_ <- printLine(s"\nfor is still running\n")
yield
()
Verification (The final, corrected code)
I want to note that I verified this solution, and this is my final, corrected code:
//> using scala "3"
//> using lib "dev.zio::zio::2.1.0"
package zio_done
import zio.*
import zio.Console.*
object ZioMinimalDoneTest extends ZIOAppDefault:
val failWithMsgEffect =
printLineError("Usage: yada yada...").flatMap { _ =>
ZIO.fail(new Exception("Usage error"))
}
val blueprint =
for
args <- ZIOAppArgs.getArgs
rez <- if args.size >= 1 then ZIO.succeed(()) else failWithMsgEffect
_ <- printLine(s"\nfor is still running\n")
yield
args(0)
def run = blueprint.foldZIO(
failure => printLineError(s"FAILURE = $failure"),
success => printLine( s"SUCCESS = $success")
)
As shown, I use an if
condition inside my blueprint
value, and everything now works as I expected. Here’s the success case output:
$ scala-cli ZioDoneTestMinimal.scala -- 42
for is still running
SUCCESS = 42
And here’s the failure case output:
$ scala-cli ZioDoneTestMinimal.scala
Usage: yada yada...
FAILURE = java.lang.Exception: Usage error
Note that in this link/example, the creator of that code throws an Exception
for the failure/error case.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
Summary
In summary, if you are having problems understanding how ZIO.cond
works, I hope this example and description is helpful.