aboutsummaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2012-12-19 12:17:08 +0100
committerJason Zaugg <jzaugg@gmail.com>2012-12-19 12:17:08 +0100
commit933657d7541bc54b9f03d86d215d274f83694b31 (patch)
tree65b86dbc74ad7f63cac68265f8e4eaeab1468691 /README.md
parent3859c11bca3ce9d9d2b18ec629dcc4b6218cf2e0 (diff)
downloadscala-async-933657d7541bc54b9f03d86d215d274f83694b31.tar.gz
scala-async-933657d7541bc54b9f03d86d215d274f83694b31.tar.bz2
scala-async-933657d7541bc54b9f03d86d215d274f83694b31.zip
Expand documentation
Diffstat (limited to 'README.md')
-rw-r--r--README.md156
1 files changed, 150 insertions, 6 deletions
diff --git a/README.md b/README.md
index d2f6b5d..39332b6 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,156 @@
-Scala Async Project
-===================
+# Scala Async Project
-Building
---------
+## Quickstart
+
+ - Add `scala-async.jar` to your classpath
+ - Use Scala 2.10.0
+
+```scala
+import ExecutionContext.Implicits.global
+import scala.async.Async.{async, await}
+
+val future = async {
+ val f1 = async { ...; true }
+ val f2 = async { ...; 42 }
+ if (await(f1)) await(f2) else 0
+}
+```
+
+## What is `async`?
+
+`async` marks a block of asynchronous code. Such a block usually contains
+one or more `await` calls, which marks a point at which the computation
+will be suspended until the awaited `Future` is complete.
+
+By default, `async` blocks operate on `scala.concurrent.{Future, Promise}`.
+The system can be adapted to alternative implementations of the
+`Future` pattern.
+
+Consider the following example:
+
+```scala
+def slowCalcFuture: Future[Int] = ... // 01
+def combined: Future[Int] = async { // 02
+ await(slowCalcFuture) + await(slowCalcFuture) // 03
+}
+val x: Int = Await.result(combined, 10.seconds) // 05
+```
+
+Lines 1 defines an asynchronous method: it returns a `Future`.
+
+Line 3 begins an `async` block. During compilation,
+the contents of this block will be analyzed to identify
+the `await` calls, and transformed into non-blocking
+code.
+
+Control flow will immediately pass to line 5, as the
+computation in the `async` block is not executed
+on the caller's thread.
+
+Line 4 begins by triggering `slowCalcFuture`, and then
+suspending until it has been calculating. Only after it
+has finished, we trigger it again, and suspend again.
+Finally, we add the results and complete `combined`, which
+in turn will release line 5 (unless it had already timed out).
+
+It is important to note that while we this code is non-blocking,
+it is not parallel. If we wanted to parallelize the two computations,
+we could rearrange the code as follows.
+
+```scala
+def combined: Future[Int] = async {
+ val future1 = slowCalcFuture
+ val future2 = slowCalcFuture
+ await(future1) + await(future2)
+}
+```
+
+## Comparison with direct use of `Future` API
+
+This computation could also be expressed by directly using the
+higher-order functions of Futures:
+
+```scala
+def slowCalcFuture: Future[Int] = ...
+val future1 = slowCalcFuture
+val future2 = slowCalcFuture
+def combined: Future[Int] = for {
+ r1 <- future1
+ r2 <- future2
+} yield r1 + r2
+```
+
+The `async` approach has two advantages over the use of
+`map` and `flatMap`.
+ 1. The code more directly reflects the programmers intent,
+ and does not require us to name the results `r1` and `r2`.
+ This advantage is even more pronounces when we mix control
+ structures in `async` blocks.
+ 2. `async` blocks are compiled to a single anonymous class,
+ as opposed to a seperate anonymous class for each closure
+ required at each generator (`<-`) in the for-comprehension.
+ This reduces the size of generated code, and can avoid boxing
+ of intermediate results.
+
+## Comparison with CPS plugin
+
+The existing continations (CPS) plugin for Scala can also be used
+to provide syntactic layer like `async`. This approach has been
+used in Akka's [Dataflow Concurrency](http://doc.akka.io/docs/akka/snapshot/scala/dataflow.html)
+
+CPS based rewriting of asynchrous code also produces an closure
+for each suspension. It can also lead to type errors that are
+difficult to understand.
+
+## How it works
+
+ - The async macro analyses the block of code, looking for control
+ structures and locations of await calls. It then breaks the code
+ into 'chunks'. Each chunk contains a linear sequence of statements
+ that concludes with branching decision, or with the registration
+ of subsequent state handler as a continuation.
+ - Before this analysis and transformation, the program is normalized
+ into a form amenable to this manipulation. This is called the
+ "A Normal Form" (ANF), and roughly means that:
+ - `if` and `match` constructs are only used as statements;
+ they cannot be used as an expression.
+ - calls to await are not allowed in compound expressions.
+ - Identify vals, vars and defs that are accessed from multiple
+ states. These will be lifted out to fields in the state machine
+ object.
+ - Synthesize a class that holds:
+ - an integer representing the current state ID
+ - the lifted definitions
+ - an `apply(value: Try[Any]): Unit` method that will be
+ called on completion of each future. This behaviour of
+ this method is determined by the current state. It records
+ the downcast result of the future in a field, and calls the
+ `resume()` method.
+ - the `resume(): Unit` method that switches on the current state
+ and runs the users code for one 'chunk', and either:
+ a) registers the state machine as the handler for the next future
+ b) completes the result Promise of the async block, if at the terminal state.
+ - an `apply(): Unit` method that starts the computation.
+
+## Troubleshooting
+ - Logging of the transform can be enabled with `scalac -Dscala.async.debug=true`.
+ - Tracing of the ANF transform: `scalac -Dscala.async.trace=true`
+ - Debug the macro expansion by checking out the project and executing the application
+ [`TreeInterrogation`](https://github.com/phaller/scala-async/blob/master/src/test/scala/scala/async/TreeInterrogation.scala#L59)
+
+## Limitations
+ - See the [neg](https://github.com/phaller/scala-async/tree/master/src/test/scala/scala/async/neg) test cases for
+ for constructs that are not allowed in a async block
+ - See the [issue list](https://github.com/phaller/scala-async/issues?state=open) for which of these restrictions are planned
+ to be dropped in the next milestone.
+ - See [#13](https://github.com/phaller/scala-async/issues/13) for why `await` is not possible in closures, and for suggestions on
+ ways to structure the code to work around this limitation.
+
+## Building
The async macro and its test suite can be built and run with SBT.
-Contributing
-------------
+## Contributing
If you are interested in contributing code, we ask you to complete and submit
to us the Scala Contributor License Agreement, which allows us to ensure that
@@ -17,3 +160,4 @@ http://www.scala-lang.org/sites/default/files/contributor_agreement.pdf
Before submitting a pull-request, please make sure you have followed the guidelines
outlined in our [Pull Request Policy](https://github.com/scala/scala/wiki/Pull-Request-Policy).
+