In this Scala example I need to stop when the result is StopNow
, I need to do this after calling decisionStep
.
How can I do that?
case class BusinessState()
trait BusinessResult
case object KeepGoing extends BusinessResult
case object StopNow extends BusinessResult
type IOState[S, A] = StateT[IO, S, A]
type BusinessIOState[A] = IOState[BusinessState, A]
trait SomeSteps {
def step1:BusinessIOState[Unit]
def step2:BusinessIOState[BusinessState]
def decisionStep:BusinessIOState[BusinessResult]
def step3:BusinessIOState[BusinessResult]
def step4:BusinessIOState[BusinessResult]
def program = for {
_ <- step1
businessState <- step2
businessResult <- decisionStep
businessResult1 <- step3
businessResult2 <- step4
} yield()
}
If you want to keep the state, then you could throw yet another monad transformer into the mix, namely
OptionT
, for short-circuiting. The entire block with all the steps that might return aStopNow
is then lifted intoOptionT
, and finally brought back toBusinessIOState
usinggetOrElse
:The output is:
Note that the
4
does not appear, the program stops earlier, becausestep3
returns aStopNow
.You might wonder why not use the capabilities of
MonadError[IO, Throwable]
for short-circuiting, sinceIO
already can deal with computations that stop because of a thrown exception. Here is what it might look like:Again, the output is:
I think that it's neither shorter nor clearer compared to the
OptionT
version, and it also has the disadvantage that in case of aStopNow
result, the state is not passed on properly, but instead everything is erased, and a()
is returned at the "end of the world". This is somewhat analogous to using exceptions for control flow, with an additional disadvantage that all it can do is to exit the whole program altogether. So, I'd probably try it withOptionT
.