在这个Scala示例中,我需要在结果为StopNow
时停止,我需要在调用decisionStep
之后执行此操作。
我该怎么做?
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()
}
答案 0 :(得分:4)
如果要保持状态,则可以将另一个monad变压器(即OptionT
)投入混合,以进行短路。然后,将包含所有可能返回StopNow
的所有步骤的整个块放到OptionT
中,最后使用BusinessIOState
带回到getOrElse
:
import cats._
import cats.data._
import cats.syntax._
import cats.effect._
object StopIO extends IOApp {
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 toOpt(a: BusinessIOState[BusinessResult])
: OptionT[BusinessIOState, BusinessResult] = {
OptionT.liftF(a).filter(_ == KeepGoing)
}
def program: BusinessIOState[Unit] = (for {
_ <- step1
businessState <- step2
_ <- (for {
_ <- toOpt(decisionStep)
_ <- toOpt(step3)
_ <- toOpt(step4)
} yield ()).getOrElse(())
} yield ())
}
object Impl extends SomeSteps {
def step1 = Monad[BusinessIOState].unit
def step2 = Monad[BusinessIOState].pure(BusinessState())
def decisionStep = StateT.liftF(IO { println("dS"); KeepGoing })
def step3 = StateT.liftF(IO { println("3"); StopNow })
def step4 = StateT.liftF(IO { println("4"); KeepGoing })
}
def run(args: List[String]) = for {
_ <- Impl.program.runA(BusinessState())
} yield ExitCode.Success
}
输出为:
dS
3
请注意,4
不会出现,程序会更早停止,因为step3
返回了StopNow
。
您可能想知道为什么不使用MonadError[IO, Throwable]
的功能来进行短路,因为IO
已经可以处理由于抛出异常而停止的计算。可能是这样的:
import cats._
import cats.data._
import cats.syntax._
import cats.effect._
object StopIO extends IOApp {
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 raiseStop(a: BusinessIOState[BusinessResult])
: BusinessIOState[Unit] = {
a.flatMap {
case KeepGoing => StateT.liftF(IO.unit)
case StopNow => StateT.liftF(
MonadError[IO, Throwable].raiseError(new Exception("stop now"))
)
}
}
def program = (for {
_ <- step1
businessState <- step2
_ <- raiseStop(decisionStep)
_ <- raiseStop(step3)
_ <- raiseStop(step4)
} yield ())
}
object Impl extends SomeSteps {
def step1 = Monad[BusinessIOState].unit
def step2 = Monad[BusinessIOState].pure(BusinessState())
def decisionStep = StateT.liftF(IO { println("dS"); KeepGoing })
def step3 = StateT.liftF(IO { println("3"); StopNow })
def step4 = StateT.liftF(IO { println("4"); KeepGoing })
}
def run(args: List[String]) = for {
_ <- Impl.program.runA(BusinessState()).handleErrorWith(_ => IO.unit)
} yield ExitCode.Success
}
同样,输出为:
dS
3
我认为与OptionT
版本相比,它既不简短也不清晰,并且它还具有以下缺点:如果结果为StopNow
,则状态不会正确传递,而是将所有内容传递给删除,并在“世界尽头”返回()
。这在某种程度上类似于对控制流使用异常,但还有一个缺点,就是它只能退出整个程序。因此,我可能会尝试使用OptionT
。