I use a standard IO monad.
And at some point, i need to shortcircuit. On a given condition, i don't want to run the following ios.
Here is my solution, but I found it too verbose and not elegant :
def shortCircuit[A](io: IO[A], continue: Boolean) =
io.map(a => if (continue) Some(a) else None)
for {
a <- io
b <- shortCircuit(io, a == 1)
c <- shortCircuit(io, b.map(_ == 1).getOrElse(false))
d <- shortCircuit(io, b.map(_ == 1).getOrElse(false))
e <- shortCircuit(io, b.map(_ == 1).getOrElse(false))
} yield …
For example, for 3rd, 4th and 5th line, I need to repeat the same condition.
Is there a better way ?
You haven't actually short-circuited anything there. You are still running the IOs; you just don't capture the values.
Also, the standard IO monad doesn't define filter
(or withFilter
), so you can't use guards in your for-comprehension.
Now, if you want just what you've said (same logic, just more DRY), you can always assign a temporary variable in the for comprehension:
for {
a <- io
b <- shortCircuit(io, a == 1)
continue = b.map(_ == 1).getOrElse(false)
c <- shortCircuit(io, continue)
d <- shortCircuit(io, continue)
e <- shortCircuit(io, continue)
} yield …
But if you actually want to short-circuit, you will have to break apart the cases in some way. Here's one possibility, assuming you just want to pack everything into an array so the return type is simple, and your IO
companion object has an apply method that you can use to create something that just returns a value:
io.flatMap(a =>
if (a == 1) IO(() => Array(a))
else io.flatMap(b =>
if (b == 1) IO(() => Array(a,b))
else for {
c <- io
d <- io
e <- io
} yield Array(a,b,c,d,e)
)
)
If your return types are more complicated, you may have to work harder with specifying types.
FWIW, it is worth noting the penalty that you pay for keeping things wrapped in monads; without, the same logic would be (for example):
io() match {
case 1 => Array(1)
case a => io() match {
case 1 => Array(a, 1)
case b => Array(a, b, io(), io(), io())
}
}
And if you allow returns, you get:
val a = io()
if (a == 1) return Array(a)
val b = io()
if (b == 1) return Array(a, b)
Array(a, b, io(), io(), io())
It's also possible in principle to decorate the IO monad with extra methods that help out a bit, but the standard withFilter
won't work so you won't be able to use the for-comprehension syntactic sugar.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With