我有一系列改变上下文状态的计算:
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来做到这一点?
答案 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
,它将是最后一个计算步骤的延续(即Continuation
将c
来自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)