scala - 自定义循环 dsl 终止于循环体中条件匹配的点
问题描述
最近我正在探索kotlin
编写 dsl,我对 kotlin 中的 dsl 支持感到非常惊讶,尤其是带有接收器模式的lambda 。我们可以通过一些隐含的魔法在 scala 中实现同样的效果吗?
有没有办法我可以写下面的 dslscala
var counter1 = 0
var counter2 = 0
loop {
counter1 += 1
println(counter1)
stopIf(counter1 == 5) // loop should terminate here and not execute rest of the code if condition matches
counter2 += 2
println(counter2)
stopIf(counter2 == 8) // loop should terminate here and not execute rest of the code if condition matches
}
这是相同的 kotlins 实现。
解决方案
通常具有控制结构的 DSL 依赖于 Scala/Haskell 中的自定义 monad,类似于(伪代码):
//I'm going to skip monad transformers and Free assuming it's "all in one monad" (state, AST, interpreters)
for {
counter1 <- variable(0)
counter2 <- variable(0)
repetition <- loop //should be trampolined to avoid stack-overflow
_ <- counter1.update(_ + 1)
counter1value <- counter1.get
_ = print(counter1value)
_ <- repetition.stopIf(counter1Value == 5)
_ <- counter2.update(_ + 2)
counter2value <- counter2.get
_ = print(counter2value)
_ <- repetition.stopIf(counter2Value == 8)
_ <- repetition.repeatForever
}
鉴于您不太可能选择这种方法,我不会详细说明,但总的来说bind
(又名 flatMap)F[X], X => F[Y]
允许您模拟“;” (分号)在编程语言中是传统的,并且在旅途中记住每一个操作,就像它是一块 AST(因为你可以在 bind's 中懒惰地得到它F[Y]
),甚至重复它。
尽管与 Haskell 不同,Scala 支持更“自然”的方式来停止中间代码的执行——即“抛出”:
import scala.util._
object StopException extends Throwable
def loop (block: => Unit) = while ({
Try(block) match {
case Failure(StopException) => false
case Failure(t) => throw t
case Success(_) => true
}
}) {}
def stopIf(condition: Boolean) = if (condition) throw StopException
Scala 使用可破坏性使其变得更加容易:
import util.control.Breaks._
def loop (block: => Unit) = breakable(while(true){block})
def stopIf(condition: Boolean) = if (condition) break
基本上,您的代码在两个定义中都按预期工作:
var counter1 = 0
var counter2 = 0
loop {
counter1 += 1
println(counter1)
stopIf(counter1 == 5)
counter2 += 2
println(counter2)
stopIf(counter2 == 8)
}
1
2
2
4
3
6
4
8
PS 您还可以使用停止异常方法和 Future 上的恢复来构建异步循环。或者,可以使用 monad 来完成延续传递风格(挂起、协程)(参见 CpsMonad)。
推荐阅读
- c# - Array.Sort() 双二维数组
- c# - HTTP 错误 500.19 - 0x8007000d - web.config
- c# - 为什么这个 `using` 块不释放数据库连接?
- sqlite - 将 CSV 解析为 DataGrip 数据库失败
- javascript - 如果属性内部不存在变量,如何正确解构变量?
- snowflake-cloud-data-platform - 用户和安全元数据
- wpf - Wpf 控制图像和视频多合一
- excel - 将 Useform 中 TextBox 中的值添加到预定义表
- android - 颤振未在完全重启时应用更改
- node.js - Electron Windows Store events.js 抛出未处理的“错误”事件