问题
I'm new to Scala continuations, and relatively new to the scala language in general.
I tried playing with Scala continuations and wrote the following code:
case class MyException(msg:String) extends Exception
def go:Int = reset {
println("enter your input")
val my_check = //try
{
val user_input = readLine()
if (!user_input.matches("\\w+")) {
throw new MyException("illegal string: " + user_input)
}
shift {
k: (Boolean => Int) => {
if (user_input == "true") {
k(true)
}
else if (user_input == "false") {
k(false)
}
else {
// don't even continue
0
}
}
}
}
// catch {
// case MyException(msg) => false
// }
if (my_check) {
println("TRUE")
1
}
else {
println("FALSE")
-1
}
}
println(go)
The code worked as expected: when the user enters a non-alphanumeric string a MyException
is thrown, when the user enters "true" the code continues with my_check = true
, when the user enters "false" the code continues with my_check = false
, and when the user enters an alphanumeric string which is not "true" nor "false" the go
function exits with 0.
Then I tried wrapping some of the code in a try-catch block (where the comments are), and the compilation failed with:
error: found cps expression in non-cps position
val my_check = try
I understand there's a problem with "injecting" an exception into a continuation, but why can't I simply put the shifted call inside a try-catch block?
I need this in the framework I'm planning, in which the programmer will not be aware that his code is used in a continuation form (he'll call some function which he would think to be "normal", but will actually do the shift
).
Obviously, I need him to be able to call the function inside a try-catch block, even though the shifted call itself will not raise an exception.
Can this issue be solved with ControlContext? Will it help if I add some "typing" rules on the values (maybe with the @cps[..])?
I already thought about the alternative of using Actors so you won't get any credit for that :)
Thanks,
(P.S. I'm using Scala 2.9.2, and obviously use the -P:continuations:enable flag)
回答1:
Thanks @som-snytt, but your solution was somewhat far from a generic one. I can't demand the framework user to write def my_check
instead of val my_check
every time it uses a try-catch block.
However, I played with your solution, and built the following code:
import scala.util.continuations._
case class MyException(msg:String) extends Exception
object try_protector {
def apply[A,B](comp: => A @cps[B]):A @cps[B] = {
comp
}
}
object Test extends App {
def go: Int = reset {
println("enter your input")
val my_check = try_protector {
try {
val user_input = readLine()
if (!user_input.matches("\\w+")) {
throw new MyException("illegal string: " + user_input)
}
shift {
k: (Boolean => Int) => {
user_input match {
case "true" => k(true)
case "false" => k(false)
case _ => 0
}
}
}
} catch {
case MyException(msg) => false
}
}
if (my_check) {
println("TRUE")
1
} else {
println("FALSE")
-1
}
}
println(go)
}
And it works! (on scala 2.9.2)
The user just has to wrap his try-catch block with a try_protector
and the code will compile.
Don't ask me how or why... It looks like compilation VODOU to me...
I haven't tried it on scala 2.10.
来源:https://stackoverflow.com/questions/12662113/scala-continuations-why-cant-my-shifted-call-be-inside-a-try-catch-block