How to implement a short circuit with an IO monad in Scala

I use the standard IO monad.

And at some point I need a short circuit. Under the given condition, I do not 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 the 3rd, 4th and 5th lines, I need to repeat the same condition.

Is there a better way?

+6
source share
1 answer

You really haven't shorted out anything. You still run the MO; you just don’t capture the values.

In addition, the standard IO monad does not define a filter (or withFilter ), so you cannot use protective devices in your understanding.

Now, if you want only what you said (same logic, just more DRY), you can always assign a temporary variable to understand:

 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 really want a short circuit, you have to somehow break things up. There is one possibility here, assuming you just want to pack everything into an array, so the return type is simple, and your companion IO 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 complex, you may need to work more with specifying types.

FWIW, it’s worth noting the punishment you pay for storing 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 a refund, 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 is also possible, in principle, to decorate the IO monad with additional methods that help a little, but the standard withFilter will not work, so you won’t be able to use syntactic sugar for understanding.

+1
source

Source: https://habr.com/ru/post/984298/


All Articles