Scala Continuations - Why can't my shifted cal

2020-07-18 05:03发布

问题:

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.