使用 State 和 IO 的堆叠 monad 时,在流程中停止理解

2024-03-02

在这个 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()
  }

如果你想保持状态,那么你可以将另一个 monad 转换器放入其中,即OptionT,用于短路。整个块以及可能返回的所有步骤StopNow然后被提升到OptionT,最后带回到BusinessIOState using 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.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 State 和 IO 的堆叠 monad 时,在流程中停止理解 的相关文章

随机推荐