Skip to content

Commit

Permalink
Merge pull request #614 from phaller/backport/cps-update-master
Browse files Browse the repository at this point in the history
Backport of the 2.10 continuations plugin to 2.9
  • Loading branch information
lrytz committed May 24, 2012
2 parents 8e7f44c + 0c99e79 commit 6891caa
Show file tree
Hide file tree
Showing 11 changed files with 736 additions and 355 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
// $Id$
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2010-2011, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */

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 @cpsParam[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
Expand All @@ -13,7 +31,55 @@ private class cpsPlus extends StaticAnnotation with TypeConstraint // implementa
private class cpsMinus extends Annotation // implementation detail



/**
* 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 type
* `A => B` to move the state to the `B` type, then the entire computation can
* be completed resulting in a value of type `C`.
*
* 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 the `continuations`
* 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 type `A => B` directly against
* the trivial value. A ''non-trivial'' value stores a computation '''around'''
* the state transformation of type `A => B` and cannot be short-circuited.
*
* @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. Allowed to be null if
* the context is non-trivial.
* @tparam A The type of the state currently held in the context.
* @tparam B The type of the transformed state needed to complete this computation.
* @tparam C The return type of the entire computation stored in this context.
* @note `fun` and `x` are allowed to be `null`.
* @see scala.util.continutations.shiftR
*/
final class ControlContext[+A,-B,+C](val fun: (A => B, Exception => B) => C, val x: A) extends Serializable {

/*
Expand All @@ -26,7 +92,12 @@ final class ControlContext[+A,-B,+C](val fun: (A => B, Exception => B) => C, val
}
*/


/**
* 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 {
Expand Down Expand Up @@ -55,6 +126,17 @@ final class ControlContext[+A,-B,+C](val fun: (A => B, Exception => B) => C, val
// 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 {
Expand All @@ -80,6 +162,11 @@ final class ControlContext[+A,-B,+C](val fun: (A => B, Exception => B) => C, val
}, 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 = {
Expand All @@ -89,8 +176,9 @@ final class ControlContext[+A,-B,+C](val fun: (A => B, Exception => B) => C, val
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?
Expand Down
134 changes: 128 additions & 6 deletions src/continuations/library/scala/util/continuations/package.scala
Original file line number Diff line number Diff line change
@@ -1,21 +1,134 @@
// $Id$


// TODO: scaladoc
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2010-2011, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */

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]


/**
* The `shift` function captures the remaining computation in a `reset` 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 expression `shift ... + 1`.
* The compiler will alter this expression so that the call
* to `shift` 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 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 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 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 continuations captured within the argument closure
* of this `reset` call and returns the result of the entire transformed
* computation. Within an expression of the form `reset { block }`,
* the closure expression (`block`) will be modified such that at 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 code that uses `shift` to capture continuations.
*/
def reset[A,C](ctx: =>(A @cpsParam[A,C])): C = {
val ctxR = reify[A,A,C](ctx)
if (ctxR.isTrivial)
Expand Down Expand Up @@ -46,6 +159,9 @@ package object continuations {
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")
}
Expand All @@ -54,6 +170,12 @@ package object continuations {
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])
}
Expand Down
Loading

0 comments on commit 6891caa

Please sign in to comment.