Jea*_*let 7 continuations scala type-inference control-flow
免责声明:在有人说出来之前:是的,我知道这是不好的风格,不鼓励.我只是这样做与Scala一起玩,并尝试更多地了解类型推理系统如何工作以及如何调整控制流.我不打算在实践中使用此代码.
所以:假设我处于一个相当冗长的函数中,在开始时有很多连续的检查,如果它们失败,它们都应该导致函数返回一些其他值(而不是throw),否则返回正常值.我不能return在身体中使用Function.但我可以模拟它吗?有点像break模拟scala.util.control.Breaks?
我想出了这个:
object TestMain {
case class EarlyReturnThrowable[T](val thrower: EarlyReturn[T], val value: T) extends ControlThrowable
class EarlyReturn[T] {
def earlyReturn(value: T): Nothing = throw new EarlyReturnThrowable[T](this, value)
}
def withEarlyReturn[U](work: EarlyReturn[U] => U): U = {
val myThrower = new EarlyReturn[U]
try work(myThrower)
catch {
case EarlyReturnThrowable(`myThrower`, value) => value.asInstanceOf[U]
}
}
def main(args: Array[String]) {
val g = withEarlyReturn[Int] { block =>
if (!someCondition)
block.earlyReturn(4)
val foo = precomputeSomething
if (!someOtherCondition(foo))
block.earlyReturn(5)
val bar = normalize(foo)
if (!checkBar(bar))
block.earlyReturn(6)
val baz = bazify(bar)
if (!baz.isOK)
block.earlyReturn(7)
// now the actual, interesting part of the computation happens here
// and I would like to keep it non-nested as it is here
foo + bar + baz + 42 // just a dummy here, but in practice this is longer
}
println(g)
}
}
Run Code Online (Sandbox Code Playgroud)
我在这里的检查显然是假的,但重点是我想避免这样的事情,实际上有趣的代码最终会因为我的口味而过于嵌套:
if (!someCondition) 4 else {
val foo = precomputeSomething
if (!someOtherCondition(foo)) 5 else {
val bar = normalize(foo)
if (!checkBar(bar)) 6 else {
val baz = bazify(bar)
if (!baz.isOK) 7 else {
// actual computation
foo + bar + baz + 42
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的解决方案在这里运行正常,如果我愿意的话,我可以提早返回4作为返回值.麻烦的是,我必须明确地写出类型参数[Int]- 这有点痛苦.有什么方法可以解决这个问题吗?
这与您的主要问题有点无关,但我认为,更有效的方法(不需要抛出异常)来实现return将涉及延续:
def earlyReturn[T](ret: T): Any @cpsParam[Any, Any] = shift((k: Any => Any) => ret)
def withEarlyReturn[T](f: => T @cpsParam[T, T]): T = reset(f)
def cpsunit: Unit @cps[Any] = ()
def compute(bool: Boolean) = {
val g = withEarlyReturn {
val a = 1
if(bool) earlyReturn(4) else cpsunit
val b = 1
earlyReturn2(4, bool)
val c = 1
if(bool) earlyReturn(4) else cpsunit
a + b + c + 42
}
println(g)
}
Run Code Online (Sandbox Code Playgroud)
这里唯一的问题是您必须显式使用cpsunit.
EDIT1:是的,earlyReturn(4, cond = !checkOK)可以实现,但不会那么通用和优雅:
def earlyReturn2[T](ret: T, cond: => Boolean): Any @cpsParam[Any, Any] =
shift((k: Any => Any) => if(cond) ret else k())
Run Code Online (Sandbox Code Playgroud)
k上面的代码片段代表了其余的计算。根据 的值cond,我们要么返回该值,要么继续计算。
EDIT2: Any chance we might get rid of cpsunit?这里的问题是,如果没有shift.insideif语句,则不允许使用该语句else。编译器拒绝转换Unit为Unit @cps[Unit].