I have been trying to wrap my head around the complex typing issues with scala continuations. I've been reading all the material I can find on it, including the reference docs on the continuations package. I think I have it figured out to some degree and it makes SOME sense when you think about it.
I think my understanding of it (and some of my question) can be best summed up by this program:
package com.whatever;
import scala.util.continuations._;
object methods {
/* The method takes an Int as its parameter. Theoretically, at some point in the future,
* it will return a Float to the remainder of the continuation. This example does it
* immediately but doesn't have to (for example it could be calling a network service
* to do the transformation)
*
* Float @cpsParam[Unit,Float] means that whatever part of the reset{} that is captured
* as a closure should receive a Float and needn't return anything (would it be meaningful
* if Unit were something else?)
*
* The reason I have to return 0.toFloat is so the compiler can properly type the
* method. That zero will never go anywhere. Is this a sign I'm doing it wrong?
*/
def method1(param:Int): Float @cpsParam[Unit,Float] = shift { cb:(Float=>Unit) =>
cb(param.toFloat);
0.toFloat;
}
/* This method is basically identical but returns a String instead of a Float (Again,
* theoretically this would be done by a network service and cb would be called at some
* point in the future.
*/
def method2(param:Int): String @cpsParam[Unit,String] = shift { cb:(String=>Unit) =>
cb(param.toString);
""
}
}
object Main {
def main(args:Array[String]):Unit = {
reset {
val f = methods.method1(5);
println(f);
}
}
}
Incidentally, it's criminal that StackOverflow doesn't highlight scala! (I stand corrected; it actually does a pretty good job but just not in the live preview)
My questions are as follows:
- Judging from the comments in the program above, what is missing from my understanding of scala's CPS? Is there ever a situation where you would NOT want
Unit
asB
in@cpsParam[B,C]
? - The above program compiles and works (it prints out
"5.0"
). But, the issue I'm running into now that's causing my confusion is when I change thereset
block to try to callmethod2
aftermethod1
:
(Apparently you can't have a code block right after a list)
reset {
val f = methods.method1(5);
println(f);
val s = methods.method2(42);
println(s);
}
When I do this (which seems like a pretty simple thing), I get the following compiler error at the reset (this is scala 2.10 Milestone 2):
illegal answer type modification: scala.util.continuations.cpsParam[Unit,Float] andThen scala.util.continuations.cpsParam[Unit,String]
I interpret this to mean "Your first shift returns a Float and your second shift returns a String and you can't do that." Is this accurate? Does that mean you cannot use CPS to do two (or more) things in sequence unless they have the same return type? Because that seems like kind of a serious limitation. I'm guessing I'm either 1) Missing something that allows you to do this, or B) Missing some obvious reason why it's impossible for that to happen with CPS. But which one is it?
I'm starting to feel less like you need to be a post-doc student in order to understand scala's CPS. But I'm certainly not quite there yet.
Unit
is dead wrong. See the paper example for cartesian product, for instance. – Nitrate