scalaz免费Monad控制中断并暂停一系列计算

时间:2017-01-15 19:28:34

标签: scala monads scalaz

我有一系列改变上下文状态的计算:

int

我想介绍计算的返回值来控制该链的流程:

case class Context(...)
type Step = (Context => Context)
val step1: Step = ctx => { ctx.copy(...) }
val step2: Step
val step3: Step
// ...
val stepN: Step
val chain = List(step1, step2, step3, ..., stepN).toStream

中断 - 表示在执行当前步骤后取消链

暂停 - 意味着在执行后暂停当前步骤的计算,能够稍后恢复下一步(并检查它是否可以恢复?)

继续 - 表示正常继续流程。

每个步骤都应返回包含在Cont。

中的上下文的当前值

目前我通过展开步骤流(val chain:Stream [Cont [Step]])并检查chain.isEmpty和chain.head以检查是否还有一些计算来做到这一点

如何在scalaz中使用免费monad来做到这一点?

1 个答案:

答案 0 :(得分:1)

您可以使用scalaz的延续monad mysql [teamcity]> SELECT id, password FROM users; +-----+---------------------------------------------------+ | id | password | +-----+---------------------------------------------------+ | 21 | k9d9yuE13FtQm8eT:1e24ad492777f94dec0c905127d1ea48 | | 13 | m1l79Yy03hjoxKdA:199d1ea48e28a78bafde576dd88e6de7 | | 85 | gOBpYHipOrtEGbUx:88f234847c07085798f9a4f8726e39df | +-----+---------------------------------------------------+ 来实现此目的。

遗憾的是,没有文档,也没有关于如何使用scalaz.Cont的示例。因此,要了解延续及其用途的基本知识,请查看

使用continuation的本质是,我们不会从计算中返回值,而是将控件传递给continuation(通常使用变量scalaz.Cont)并且continuation指定接下来会发生什么。

这就是说,让我们看一下你的具体例子中的情况:

  • 首先是一些类型定义;请注意,k,即“下一步”是一个函数,它接受Continuation并返回Context类型的内容

    Return
  • 然后定义一些类型来控制计算链中的流程;请注意,case class Context(i: Int) type Continuation = Context => Return 也会返回Pause,因为我们希望以后能够继续计算

    Continuation
  • 接下来我们将我们的步骤定义为类,以便能够将它们与Free monad一起使用...

    trait Return
    case class Break(context: Context) extends Return
    case class Pause(context: Context, continuation: Continuation) extends Return
    case class Done(context: Context) extends Return
    
  • ...并将它们提升到Free monad

    的上下文中
    trait Step[A]
    case class Step1(c: Context) extends Step[Context]
    case class Step2(c: Context) extends Step[Context]
    case class Step3(c: Context) extends Step[Context]
    
  • 现在我们建立了我们的计算链;请注意,我们将先前计算的结果(例如type Command[A] = Free[Step, A] protected implicit def liftStep[A](step: Step[A]): Command[A] = Free.liftF(step) def step1(c: Context): Command[Context] = Free.liftF(Step1(c)) def step2(c: Context): Command[Context] = Free.liftF(Step2(c)) def step3(c: Context): Command[Context] = Free.liftF(Step3(c)) )传递给下一个(例如c1);如果这种情况应该更加隐含地发生,也许应该考虑将状态monad从继续状态转换为继续状态

    step2(c1)
  • 并定义一个解释器(即val script = for { c0 <- Free.point(Context(0)) c1 <- step1(c0) c2 <- step2(c1) c3 <- step3(c2) } yield c3 ),用于评估单个步骤;请参阅注释,了解单个步骤如何控制计算流程

    step
  • 使用解释器type Process[A] = Cont[Return, A] val step: Step ~> Process = new (Step ~> Process) { override def apply[A](action: Step[A]): Process[A] = action match { case Step1(c) => Cont { k: Continuation => // call next computation with context value set to 1 k(c.copy(i = 1)) } case Step2(c) => Cont { k: Continuation => // pause computation ; to be able to resume, continuation k is returned Pause(c.copy(i = 2), k) } case Step3(c) => Cont { k: Continuation => // after the pause, the computation is resumed here k(c.copy(i = 3)) // alternatively: break computation (i.e. not resumable) //Break(c.copy(i = 3)) } } } 运行script以获得延续

    step
  • 运行续集val process: Cont[Return, Context] = script.foldMap(step) ;请注意,run方法需要process,它将是最后一个计算步骤的延续(即Continuationc来自Context(3)

    Step3
  • 最后,编写一些代码来执行完整的链,如果计算刚暂停则继续,或者如果结果为var result = process.run(c => Done(c)) Break则停止

    Done

结果是:

var continue = true
do {
  continue = result match {
    case Pause(c, continuation) =>
      println(s"Pause: $c")
      result = continuation(c)
      true
    case Break(c) =>
      println(s"Break: $c")
     false
    case Done(c) =>
      println(s"Done: $c")
     false
   }
} while(continue)