package continuations
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.
CPS Annotation
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.
- Alphabetic
- By Inheritance
- continuations
- AnyRef
- Any
- Hide All
- Show All
- Public
- All
Type Members
-
final
class
ControlContext
[+A, -B, +C] extends Serializable
This class represent a portion of computation that has a 'hole' in it.
This class represent a portion of computation that has a 'hole' in it. The class has the ability to compute state up until a certain point where the state has the
A
type. If this context is given a function of typeA => B
to move the state to theB
type, then the entire computation can be completed resulting in a value of typeC
.An Example:
val cc = new ControlContext[String, String, String]( fun = { (f: String=>String, err: Exception => String) => val updatedState = try f("State") catch { case e: Exception => err(e) } updatedState + "-Complete!" }, x = null.asIntanceOf[String] } cc.foreach(_ + "-Continued") // Results in "State-Continued-Complete!"
This class is used to transform calls to
shift
in thecontinuations
package. Direct use and instantiation is possible, but usually reserved for advanced cases.A context may either be trivial or non-trivial. A trivial context just has a state of type
A
. When completing the computation, it's only necessary to use the function of typeA => B
directly against the trivial value. A non-trivial value stores a computation around the state transformation of typeA => B
and cannot be short-circuited.- A
The type of the state currently held in the context.
- B
The type of the transformed state needed to complete this computation.
- C
The return type of the entire computation stored in this context.
- Note
fun
andx
are allowed to benull
.- See also
scala.util.continutations.shiftR
-
type
cps[A] = cpsParam[A, A]
An annotation that denotes a type is part of a continuation context.
An annotation that denotes a type is part of a continuation context.
@cps[A]
is shorthand forcpsParam[A,A]
. -
class
cpsParam
[-B, +C] extends Annotation with StaticAnnotation with TypeConstraint
This annotation is used to mark a parameter as part of a continuation context.
This annotation is used to mark a parameter as part of a continuation context.
The type
A @cpsParam[B,C]
is desugared toControlContext[A,B,C]
at compile time.- B
The type of computation state after computation has executed, and before control is returned to the shift.
- C
The eventual return type of this delimited compuation.
- See also
scala.util.continuations.ControlContext
-
type
suspendable = cpsParam[Unit, Unit]
An annotation that denotes a type is part of a side effecting continuation context.
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]
.
Value Members
-
def
reify[A, B, C](ctx: ⇒ A @scala.util.continuations.cpsParam[B,C]): ControlContext[A, B, C]
This method converts from the sugared
A @cpsParam[B,C]
type to the desugaredControlContext[A,B,C]
type.This method converts from the sugared
A @cpsParam[B,C]
type to the desugaredControlContext[A,B,C]
type. The underlying data is not changed. - def reifyR[A, B, C](ctx: ⇒ ControlContext[A, B, C]): ControlContext[A, B, C]
-
def
reset[A, C](ctx: ⇒ A @scala.util.continuations.cpsParam[A,C]): C
Creates a context for continuations captured within the argument closure of this
reset
call and returns the result of the entire transformed computation.Creates a context for continuations captured within the argument closure of this
reset
call and returns the result of the entire transformed computation. Within an expression of the formreset { block }
, the closure expression (block
) will be modified such that at each call toshift
the remainder of the expression is transformed into a function to be passed into the shift.- returns
The result of a block of code that uses
shift
to capture continuations.
- def reset0[A](ctx: ⇒ A @scala.util.continuations.cpsParam[A,A]): A
- def run[A](ctx: ⇒ Any @scala.util.continuations.cpsParam[Unit,A]): A
-
def
shift[A, B, C](fun: ((A) ⇒ B) ⇒ C): A @scala.util.continuations.cpsParam[B,C]
The
shift
function captures the remaining computation in areset
block and passes it to a closure provided by the user.The
shift
function captures the remaining computation in areset
block and passes it to a closure provided by the user.For example:
reset { shift { (k: Int => Int) => k(5) } + 1 }
In this example,
shift
is used in the expressionshift ... + 1
. The compiler will alter this expression so that the call toshift
becomes a parameter to a function, creating something like:{ (k: Int => Int) => k(5) } apply { _ + 1 }
The result of this expression is 6.
There can be more than one
shift
call in areset
block. Each call toshift
can alter the return type of expression within the reset block, but will not change the return type of the entirereset { block }
expression.- fun
A function where
- The parameter is the remainder of computation within the current
reset
block. This is passed as a functionA => B
. - The return is the return value of the
ControlContext
which is generated from this inversion.
- The parameter is the remainder of computation within the current
- Note
Must be invoked in the context of a call to
reset
This context may not be far up the stack, but a call to reset is needed to eventually remove the@cps
annotations from types.
-
def
shiftR[A, B, C](fun: ((A) ⇒ B) ⇒ C): ControlContext[A, B, C]
Captures a computation into a
ControlContext
.Captures a computation into a
ControlContext
.- fun
The function which accepts the inverted computation and returns a final result.
- See also
shift
- def shiftUnit[A, B, C >: B](x: A): A @scala.util.continuations.cpsParam[B,C]
- def shiftUnit0[A, B](x: A): A @scala.util.continuations.cpsParam[B,B]
- def shiftUnitR[A, B](x: A): ControlContext[A, B, B]