Created
September 17, 2011 19:27
-
-
Save jsuereth/1224264 to your computer and use it in GitHub Desktop.
Docspree comments for ControlContext
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// $Id$ | |
package scala.util.continuations | |
import annotation.{ Annotation, StaticAnnotation, TypeConstraint } | |
/** This annotation is used to mark a parameter as part of a continuation context. | |
* | |
* The type `A @cps[B,C]` is desugared to `ControlContext[A,B,C]` at compile time. | |
* | |
* @tparam B The type of computation state after computation has executed, and before control is returned to the shift. | |
* @tparam C The eventual return type of this delimited compuation. | |
* @see scala.util.continuations.ControlContext | |
*/ | |
class cpsParam[-B,+C] extends StaticAnnotation with TypeConstraint | |
private class cpsSym[B] extends Annotation // implementation detail | |
private class cpsSynth extends Annotation // implementation detail | |
private class cpsPlus extends StaticAnnotation with TypeConstraint // implementation detail | |
private class cpsMinus extends Annotation // implementation detail | |
/** | |
* This class represents the context through which CPS transformed code is passed. | |
* | |
* @param fun The captured computation so far. The type | |
* `(A => B, Exception => B) => C` is a function where: | |
* - The first parameter `A=>B` represents the computation defined against | |
* the current state held in the ControlContext. | |
* - The second parameter `Exception => B` represents a computation to | |
* perform if an exception is thrown from the first parameter's computation. | |
* - The return value is the result of the entire computation contained in this | |
* `ControlContext`. | |
* @param x The current state stored in this context. | |
* @tparam A The type of the state held in the context. | |
* @tparam B The type of the state after the stored continuation has executed. | |
* @tparam C The return type of the entire computation has executed. | |
* @note fun is allowed to be null. | |
*/ | |
final class ControlContext[+A,-B,+C](val fun: (A => B, Exception => B) => C, val x: A) extends Serializable { | |
/* | |
final def map[A1](f: A => A1): ControlContext[A1,B,C] = { | |
new ControlContext((k:(A1 => B)) => fun((x:A) => k(f(x))), null.asInstanceOf[A1]) | |
} | |
final def flatMap[A1,B1<:B](f: (A => ControlContext[A1,B1,B])): ControlContext[A1,B1,C] = { | |
new ControlContext((k:(A1 => B1)) => fun((x:A) => f(x).fun(k))) | |
} | |
*/ | |
/** | |
* Modifies the currently captured state in this `ControlContext`. | |
* @tparam A1 The new type of state in this context. | |
* @param f A transformation function on the current state of the `ControlContext`. | |
* @return The new `ControlContext`. | |
*/ | |
@noinline final def map[A1](f: A => A1): ControlContext[A1,B,C] = { | |
if (fun eq null) | |
try { | |
new ControlContext(null, f(x)) // TODO: only alloc if f(x) != x | |
} catch { | |
case ex: Exception => | |
new ControlContext((k: A1 => B, thr: Exception => B) => thr(ex).asInstanceOf[C], null.asInstanceOf[A1]) | |
} | |
else | |
new ControlContext({ (k: A1 => B, thr: Exception => B) => | |
fun( { (x:A) => | |
var done = false | |
try { | |
val res = f(x) | |
done = true | |
k(res) | |
} catch { | |
case ex: Exception if !done => | |
thr(ex) | |
} | |
}, thr) | |
}, null.asInstanceOf[A1]) | |
} | |
// it would be nice if @inline would turn the trivial path into a tail call. | |
// unfortunately it doesn't, so we do it ourselves in SelectiveCPSTransform | |
/** | |
* Maps and flattens this `ControlContext` with another `ControlContext` generated from the current state. | |
* @note: The resulting comuptation is still the type `C`. | |
* @tparam A1 The new type of the contained state. | |
* @tparam B1 The new type of the state after the stored continuation has executed. | |
* @tparam C1 The result type of the nested `ControlContext`. Because the nested `ControlContext` is executed within | |
* the outer `ControlContext`, this type must `>: B` so that the resulting nested computation can be fed through | |
* the current continuation. | |
* @param f A transformation function from the current state to a nested `ControlContext`. | |
* @return The transformed `ControlContext`. | |
*/ | |
@noinline final def flatMap[A1,B1,C1<:B](f: (A => ControlContext[A1,B1,C1])): ControlContext[A1,B1,C] = { | |
if (fun eq null) | |
try { | |
f(x).asInstanceOf[ControlContext[A1,B1,C]] | |
} catch { | |
case ex: Exception => | |
new ControlContext((k: A1 => B1, thr: Exception => B1) => thr(ex).asInstanceOf[C], null.asInstanceOf[A1]) | |
} | |
else | |
new ControlContext({ (k: A1 => B1, thr: Exception => B1) => | |
fun( { (x:A) => | |
var done = false | |
try { | |
val ctxR = f(x) | |
done = true | |
val res: C1 = ctxR.foreachFull(k, thr) // => B1 | |
res | |
} catch { | |
case ex: Exception if !done => | |
thr(ex).asInstanceOf[B] // => B NOTE: in general this is unsafe! | |
} // However, the plugin will not generate offending code | |
}, thr.asInstanceOf[Exception=>B]) // => B | |
}, null.asInstanceOf[A1]) | |
} | |
/** | |
* Runs the computation against the state stored in this `ControlContext`. | |
* @param f the computation that modifies the current state of the context. | |
* @note This method could throw exceptions from the computations. | |
*/ | |
final def foreach(f: A => B) = foreachFull(f, throw _) | |
def foreachFull(f: A => B, g: Exception => B): C = { | |
if (fun eq null) | |
f(x).asInstanceOf[C] | |
else | |
fun(f, g) | |
} | |
/** @return true if this context only stores a state value and not any deferred computation. */ | |
final def isTrivial = fun eq null | |
/** @return The current state value. */ | |
final def getTrivialValue = x.asInstanceOf[A] | |
// need filter or other functions? | |
final def flatMapCatch[A1>:A,B1<:B,C1>:C<:B1](pf: PartialFunction[Exception, ControlContext[A1,B1,C1]]): ControlContext[A1,B1,C1] = { | |
if (fun eq null) | |
this | |
else { | |
val fun1 = (ret1: A1 => B1, thr1: Exception => B1) => { | |
val thr: Exception => B1 = { t: Exception => | |
var captureExceptions = true | |
try { | |
if (pf.isDefinedAt(t)) { | |
val cc1 = pf(t) | |
captureExceptions = false | |
cc1.foreachFull(ret1, thr1) // Throw => B | |
} else { | |
captureExceptions = false | |
thr1(t) // Throw => B1 | |
} | |
} catch { | |
case t1: Exception if captureExceptions => thr1(t1) // => E2 | |
} | |
} | |
fun(ret1, thr)// fun(ret1, thr) // => B | |
} | |
new ControlContext(fun1, null.asInstanceOf[A1]) | |
} | |
} | |
final def mapFinally(f: () => Unit): ControlContext[A,B,C] = { | |
if (fun eq null) { | |
try { | |
f() | |
this | |
} catch { | |
case ex: Exception => | |
new ControlContext((k: A => B, thr: Exception => B) => thr(ex).asInstanceOf[C], null.asInstanceOf[A]) | |
} | |
} else { | |
val fun1 = (ret1: A => B, thr1: Exception => B) => { | |
val ret: A => B = { x: A => | |
var captureExceptions = true | |
try { | |
f() | |
captureExceptions = false | |
ret1(x) | |
} catch { | |
case t1: Exception if captureExceptions => thr1(t1) | |
} | |
} | |
val thr: Exception => B = { t: Exception => | |
var captureExceptions = true | |
try { | |
f() | |
captureExceptions = false | |
thr1(t) | |
} catch { | |
case t1: Exception if captureExceptions => thr1(t1) | |
} | |
} | |
fun(ret, thr1) | |
} | |
new ControlContext(fun1, null.asInstanceOf[A]) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// $Id$ | |
package scala.util | |
/* TODO: better documentation of return-type modification. | |
* (Especially what means "Illegal answer type modification: ... andThen ...") | |
*/ | |
/** | |
* Delimited continuations are a feature for modifying the usual control flow | |
* of a program. To use continuations, provide the option `-P:continuations:enable` | |
* to the Scala compiler or REPL to activate the compiler plugin. | |
* | |
* Below is an example of using continuations to suspend execution while awaiting | |
* user input. Similar facilities are used in so-called continuation-based web frameworks. | |
* | |
* {{{ | |
* def go = | |
* reset { | |
* println("Welcome!") | |
* val first = ask("Please give me a number") | |
* val second = ask("Please enter another number") | |
* printf("The sum of your numbers is: %d\n", first + second) | |
* } | |
* }}} | |
* | |
* The `reset` is provided by this package and delimits the extent of the transformation. | |
* The `ask` is a function that will be defined below. Its effect is to issue a prompt | |
* and then suspend execution awaiting user input. Once the user provides an input value, | |
* execution of the suspended block resumes. | |
* | |
* {{{ | |
* val sessions = new HashMap[UUID, Int=>Unit] | |
* def ask(prompt: String): Int @cps[Unit] = | |
* shift { | |
* k: (Int => Unit) => { | |
* val id = uuidGen | |
* printf("%s\nrespond with: submit(0x%x, ...)\n", prompt, id) | |
* sessions += id -> k | |
* } | |
* } | |
* }}} | |
* | |
* The type of `ask` includes a `@cps` annotation which drives the transformation. | |
* The type signature `Int @cps[Unit]` means that `ask` should be used in a context | |
* requiring an `Int`, but actually it will suspend and return `Unit`. | |
* | |
* The computation leading up to the first `ask` is executed normally. The remainder | |
* of the reset block is wrapped into a closure that is passed as the parameter `k` to | |
* the `shift` function, which can then decide whether and how to execute the | |
* continuation. In this example, the continuation is stored in a sessions map for | |
* later execution. This continuation includes a second call to `ask`, which is treated | |
* likewise once the execution resumes. | |
* | |
* <h2>CPS Annotation</h2> | |
* | |
* The aforementioned `@cps[A]` annotation is an alias for the more general `@cpsParam[B,C]` | |
* where `B=C`. The type `A @cpsParam[B,C]` describes a term which yields a value of type `A` within | |
* an evaluation context producing a value of type `B`. After the CPS transformation, this return | |
* type is modified to `C`. | |
* | |
* The `@cpsParam` annotations are introduced by `shift` blocks, and propagate via the return | |
* types to the dynamically enclosing context. The propagation stops upon reaching a `reset` | |
* block. | |
*/ | |
package object continuations { | |
/** An annotation that denotes a type is part of a continuation context. | |
* `@cps[A]` is shorthand for `cpsParam[A,A]`. | |
* @tparam A The return type of the continuation context. | |
*/ | |
type cps[A] = cpsParam[A,A] | |
/** An annotation that denotes a type is part of a side effecting continuation context. | |
* `@suspendable` is shorthand notation for `@cpsParam[Unit,Unit]` or `@cps[Unit]`. | |
*/ | |
type suspendable = cps[Unit] | |
/** | |
* Inverts the the context in which it is run. The `shift` function is used | |
* to capture the remaining computation in a reset block for its own | |
* computation. For example: | |
* | |
* {{{ | |
* println(reset { | |
* shift { (k: Int => Int ) => k(5) } + 1 | |
* } | |
* }}} | |
* | |
* In this example, `shift` is used in the expression `shift ... + 1`. | |
* The continuations plugin will alter this expression so that the call | |
* to `shift` becomes a parameter to a function, creating: | |
* {{{ | |
* val f: Int => Int = _ + 1 | |
* { (k: Int => Int) => k(5) } apply f | |
* }}} | |
* The result of this expression is 6. | |
* | |
* There can be more than one `shift` call in a `reset` block. Each call | |
* to `shift` can alter the return type of expression within the reset block, | |
* but will not change the return type of the entire `reset { block }` | |
* expression. | |
* | |
* @param fun A function where | |
* - The parameter is the remainder of computation within the current | |
* `reset` block. This is passed as a function `A => B`. | |
* - The return is the return value of the `ControlContext` which is | |
* generated from this inversion. | |
* @note: Must be nested in the context of a call to reset, unless manually | |
* using `ControlContext` with `reify`. | |
*/ | |
def shift[A,B,C](fun: (A => B) => C): A @cpsParam[B,C] = { | |
throw new NoSuchMethodException("this code has to be compiled with the Scala continuations plugin enabled") | |
} | |
/** Creates a context for cps transformations and returns the result. | |
* Within a `reset { block }` block expression, code will be transformed | |
* such that after each call to `shift` the remainder of the expression is | |
* transformed into a function to be passed into the shift. | |
* @return The result of a block of cps transformed code. | |
*/ | |
def reset[A,C](ctx: =>(A @cpsParam[A,C])): C = { | |
val ctxR = reify[A,A,C](ctx) | |
if (ctxR.isTrivial) | |
ctxR.getTrivialValue.asInstanceOf[C] | |
else | |
ctxR.foreach((x:A) => x) | |
} | |
def reset0[A](ctx: =>(A @cpsParam[A,A])): A = reset(ctx) | |
def run[A](ctx: =>(Any @cpsParam[Unit,A])): A = { | |
val ctxR = reify[Any,Unit,A](ctx) | |
if (ctxR.isTrivial) | |
ctxR.getTrivialValue.asInstanceOf[A] | |
else | |
ctxR.foreach((x:Any) => ()) | |
} | |
// methods below are primarily implementation details and are not | |
// needed frequently in client code | |
def shiftUnit0[A,B](x: A): A @cpsParam[B,B] = { | |
shiftUnit[A,B,B](x) | |
} | |
def shiftUnit[A,B,C>:B](x: A): A @cpsParam[B,C] = { | |
throw new NoSuchMethodException("this code has to be compiled with the Scala continuations plugin enabled") | |
} | |
/** This method converts from the sugared `A @cpsParam[B,C]` type to the desugared | |
* `ControlContext[A,B,C]` type. The underlying data is not changed. | |
*/ | |
def reify[A,B,C](ctx: =>(A @cpsParam[B,C])): ControlContext[A,B,C] = { | |
throw new NoSuchMethodException("this code has to be compiled with the Scala continuations plugin enabled") | |
} | |
def shiftUnitR[A,B](x: A): ControlContext[A,B,B] = { | |
new ControlContext(null, x) | |
} | |
/** | |
* Captures a computation into a `ControlContext`. | |
* @param fun The function which accepts the inverted computation and returns | |
* a final result. | |
* @see shift | |
*/ | |
def shiftR[A,B,C](fun: (A => B) => C): ControlContext[A,B,C] = { | |
new ControlContext((f:A=>B,g:Exception=>B) => fun(f), null.asInstanceOf[A]) | |
} | |
def reifyR[A,B,C](ctx: => ControlContext[A,B,C]): ControlContext[A,B,C] = { | |
ctx | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment