More work on Dataflow docs

This commit is contained in:
Viktor Klang 2012-09-20 16:30:48 +02:00
parent 1380abc807
commit 45d0850e11
2 changed files with 152 additions and 14 deletions

View file

@ -0,0 +1,78 @@
/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.dataflow
import language.postfixOps
import scala.concurrent.util.duration._
import scala.concurrent.{ Await, ExecutionContext, Future, Promise }
import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
import util.{ Try, Failure, Success }
object DataflowDocSpec {
}
class DataflowDocSpec extends WordSpec with MustMatchers {
//#import-akka-dataflow
import akka.dataflow._ //to get the flow method and implicit conversions
//#import-akka-dataflow
//#import-global-implicit
import scala.concurrent.ExecutionContext.Implicits.global
//#global-implicit
import DataflowDocSpec._
"demonstrate flow using hello world" in {
def println[T](any: Try[T]): Unit = any.get must be === "Hello world!"
//#simplest-hello-world
flow { "Hello world!" } onComplete println
//#simplest-hello-world
//#nested-hello-world-a
flow {
val f1 = flow { "Hello" }
f1() + " world!"
} onComplete println
//#nested-hello-world-a
//#nested-hello-world-b
flow {
val f1 = flow { "Hello" }
val f2 = flow { "world!" }
f1() + " " + f2()
} onComplete println
//#nested-hello-world-b
}
"demonstrate the use of dataflow variables" in {
def println[T](any: Try[T]): Unit = any.get must be === 20
//#dataflow-variable-a
flow {
val v1, v2 = Promise[Int]()
// v1 will become the value of v2 + 10 when v2 gets a value
v1 << v2() + 10
v2 << flow { 5 } // As you can see, no blocking!
v1() + v2()
} onComplete println
//#dataflow-variable-a
}
"demonstrate the difference between for and flow" in {
def println[T](any: Try[T]): Unit = any.get must be === 2
//#for-vs-flow
val f1, f2 = Future { 1 }
val usingFor = for { v1 f1; v2 f2 } yield v1 + v2
val usingFlow = flow { f1() + f2() }
usingFor onComplete println
usingFlow onComplete println
//#for-vs-flow
}
}

View file

@ -4,9 +4,13 @@ Dataflow Concurrency (Scala)
Description Description
----------- -----------
Akka implements `Oz-style dataflow concurrency <http://www.mozart-oz.org/documentation/tutorial/node8.html#chapter.concurrency>`_ by using a special API for :ref:`futures-scala` that allows single assignment variables and multiple lightweight (event-based) processes/threads. Akka implements `Oz-style dataflow concurrency <http://www.mozart-oz.org/documentation/tutorial/node8.html#chapter.concurrency>`_ by using a special API for :ref:`futures-scala` that enables a complimentary way of writing synchronous-looking code that in reality is asynchronous.
Dataflow concurrency is deterministic. This means that it will always behave the same. If you run it once and it yields output 5 then it will do that **every time**, run it 10 million times, same result. If it on the other hand deadlocks the first time you run it, then it will deadlock **every single time** you run it. Also, there is **no difference** between sequential code and concurrent code. These properties makes it very easy to reason about concurrency. The limitation is that the code needs to be side-effect free, e.g. deterministic. You can't use exceptions, time, random etc., but need to treat the part of your program that uses dataflow concurrency as a pure function with input and output. The benefit of Dataflow concurrency is that it is deterministic; that means that it will always behave the same.
If you run it once and it yields output 5 then it will do that **every time**, run it 10 million times - same result.
If it on the other hand deadlocks the first time you run it, then it will deadlock **every single time** you run it.
Also, there is **no difference** between sequential code and concurrent code. These properties makes it very easy to reason about concurrency.
The limitation is that the code needs to be side-effect free, e.g. deterministic. You can't use exceptions, time, random etc., but need to treat the part of your program that uses dataflow concurrency as a pure function with input and output.
The best way to learn how to program with dataflow variables is to read the fantastic book `Concepts, Techniques, and Models of Computer Programming <http://www.info.ucl.ac.be/%7Epvr/book.html>`_. By Peter Van Roy and Seif Haridi. The best way to learn how to program with dataflow variables is to read the fantastic book `Concepts, Techniques, and Models of Computer Programming <http://www.info.ucl.ac.be/%7Epvr/book.html>`_. By Peter Van Roy and Seif Haridi.
@ -26,21 +30,77 @@ You will also need to include a dependency on akka-dataflow
.. code-block:: scala .. code-block:: scala
"com.typesafe.akka" % "akka-dataflow" % "2.1-SNAPSHOT" "com.typesafe.akka" %% "akka-dataflow" % "2.1-SNAPSHOT" cross CrossVersion.full
The flow
--------
The ``flow`` construct acts as the delimeter of dataflow expressions (this also neatly aligns with the concept of delimited continuations),
and flow-expressions compose. At this point you might wonder what the ``flow``-construct brings to the table that for-comprehensions don't,
and that is the use of the CPS plugin that makes the look _look like_ it is synchronous, but it indeed isn't when it gets executed.
import scala.concurrent.ExecutionContext.Implicit._
flow { 5 } onComplete println
Dataflow variables Dataflow variables
------------------ ------------------
A Dataflow variable can be read any number of times but only be written to once, which maps very well to the concept of Futures :ref:`futures-scala`. A Dataflow variable can be read any number of times but only be written to once, which maps very well to the concept of Futures :ref:`futures-scala`.
Conversion from ``Future`` and ``Promise`` to Dataflow is implicit and is invisible to the user. Conversion from ``Future`` and ``Promise`` to Dataflow Variables is implicit and is invisible to the user (after importing akka.dataflow._).
The mapping from ``Promise`` and ``Future`` is as follows:
- Futures are readable-any, using the ``apply`` method, inside ``flow`` blocks.
- Promises are readable-any, just like Futures.
- Promises are writable-once, using the ``<<`` operator, inside ``flow`` blocks.
Writing to an already written Promise throws a ``java.lang.IllegalStateException``,
this has the effect that races to write a promise will be deterministic,
only one of the writers will succeed and the others will fail.
The flow
--------
The ``flow`` method acts as the delimiter of dataflow expressions (this also neatly aligns with the concept of delimited continuations),
and flow-expressions compose. At this point you might wonder what the ``flow``-construct brings to the table that for-comprehensions don't,
and that is the use of the CPS plugin that makes the look _look like_ it is synchronous, but it indeed isn't when it gets executed.
The result of a call to ``flow`` is a Future with the resulting value of the flow.
To be able to use the ``flow`` method, you need to import:
.. includecode:: code/docs/dataflow/DataflowDocSpec.scala
:include: import-akka-dataflow
The ``flow`` method will, just like Futures and Promises, require an implicit ``ExecutionContext`` in scope. for the examples here we will use:
.. includecode:: code/docs/dataflow/DataflowDocSpec.scala
:include: import-global-implicit
Using flow
~~~~~~~~~~
First off we have the obligatory "Hello world!":
.. includecode:: code/docs/dataflow/DataflowDocSpec.scala
:include: simplest-hello-world
You can also refer to the results of other flows within flows:
.. includecode:: code/docs/dataflow/DataflowDocSpec.scala
:include: nested-hello-world-a
… or:
.. includecode:: code/docs/dataflow/DataflowDocSpec.scala
:include: nested-hello-world-b
Working with variables
~~~~~~~~~~~~~~~~~~~~~~
Inside the flow method you can use Promises as Dataflow variables:
.. includecode:: code/docs/dataflow/DataflowDocSpec.scala
:include: #dataflow-variable-a
Flow compared to for
--------------------
Should I use Dataflow or for-comprehensions?
.. includecode:: code/docs/dataflow/DataflowDocSpec.scala
:include: #for-vs-flow
Conclusions:
- Dataflow has a smaller code footprint and arguably is easier to reason about.
- For-comprehensions are more general than Dataflow, and can operate on a wide array of types.