Skip to content

Instantly share code, notes, and snippets.

@jsuereth
Created September 17, 2011 19:27
Show Gist options
  • Save jsuereth/1224264 to your computer and use it in GitHub Desktop.
Save jsuereth/1224264 to your computer and use it in GitHub Desktop.
Docspree comments for ControlContext
// $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])
}
}
}
// $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