diff --git a/akka-docs-dev/rst/java/stream-graphs.rst b/akka-docs-dev/rst/java/stream-graphs.rst index 9619620758..9c3ffe6f37 100644 --- a/akka-docs-dev/rst/java/stream-graphs.rst +++ b/akka-docs-dev/rst/java/stream-graphs.rst @@ -27,19 +27,19 @@ Akka Streams currently provide these junctions: * **Fan-out** - - ``Broadcast`` – (1 input, n outputs) signals each output given an input signal, - - ``Balance`` – (1 input => n outputs), signals one of its output ports given an input signal, - - ``UnZip`` – (1 input => 2 outputs), which is a specialized element which is able to split a stream of ``Pair`` into two streams one type ``A`` and one of type ``B``, - - ``FlexiRoute`` – (1 input, n outputs), which enables writing custom fan out elements using a simple DSL, + - ``Broadcast[T]`` – *(1 input, N outputs)* given an input element emits to each output + - ``Balance[T]`` – *(1 input, N outputs)* given an input element emits to one of its output ports + - ``UnZip[A,B]`` – *(1 input, 2 outputs)* splits a stream of ``(A,B)`` tuples into two streams, one of type ``A`` and one of type ``B`` + - ``FlexiRoute[In]`` – *(1 input, N outputs)* enables writing custom fan out elements using a simple DSL * **Fan-in** - - ``Merge`` – (n inputs , 1 output), picks signals randomly from inputs pushing them one by one to its output, - - ``MergePreferred`` – like :class:`Merge` but if elements are available on ``preferred`` port, it picks from it, otherwise randomly from ``others``, - - ``ZipWith`` – (n inputs (defined upfront), 1 output), which takes a function of n inputs that, given all inputs are signalled, transforms and emits 1 output, - - ``Zip`` – (2 inputs, 1 output), which is a :class:`ZipWith` specialised to zipping input streams of ``A`` and ``B`` into a ``Pair`` stream, - - ``Concat`` – (2 inputs, 1 output), which enables to concatenate streams (first consume one, then the second one), thus the order of which stream is ``first`` and which ``second`` matters, - - ``FlexiMerge`` – (n inputs, 1 output), which enables writing custom fan-in elements using a simple DSL. + - ``Merge[In]`` – *(N inputs , 1 output)* picks randomly from inputs pushing them one by one to its output + - ``MergePreferred[In]`` – like :class:`Merge` but if elements are available on ``preferred`` port, it picks from it, otherwise randomly from ``others`` + - ``ZipWith[A,B,...,Out]`` – *(N inputs, 1 output)* which takes a function of N inputs that given a value for each input emits 1 output element + - ``Zip[A,B]`` – *(2 inputs, 1 output)* is a :class:`ZipWith` specialised to zipping input streams of ``A`` and ``B`` into an ``(A,B)`` tuple stream + - ``Concat[A]`` – *(2 inputs, 1 output)* concatenates two streams (first consume one, then the second one) + - ``FlexiMerge[Out]`` – *(N inputs, 1 output)* enables writing custom fan-in elements using a simple DSL One of the goals of the FlowGraph DSL is to look similar to how one would draw a graph on a whiteboard, so that it is simple to translate a design from whiteboard to code and be able to relate those two. Let's illustrate this by translating @@ -49,7 +49,7 @@ the below hand drawn graph into Akka Streams: Such graph is simple to translate to the Graph DSL since each linear element corresponds to a :class:`Flow`, and each circle corresponds to either a :class:`Junction` or a :class:`Source` or :class:`Sink` if it is beginning -or ending a :class:`Flow`. Those are connected with the ``addEdge`` method of the :class:`FlowGraphBuilder`. +or ending a :class:`Flow`. .. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/stream/FlowGraphDocTest.java#simple-flow-graph @@ -58,14 +58,23 @@ or ending a :class:`Flow`. Those are connected with the ``addEdge`` method of th refers to the same location in the resulting graph). -By looking at the snippets above, it should be apparent that the :class:`FlowGraphBuilder` object is *mutable*. +By looking at the snippets above, it should be apparent that the ``builder`` object is *mutable*. The reason for this design choice is to enable simpler creation of complex graphs, which may even contain cycles. -Once the FlowGraph has been constructed though, the :class:`FlowGraph` instance *is immutable, thread-safe, and freely shareable*. +Once the FlowGraph has been constructed though, the :class:`RunnableFlow` instance *is immutable, thread-safe, and freely shareable*. +The same is true of all flow pieces—sources, sinks, and flows—once they are constructed. +This means that you can safely re-use one given Flow in multiple places in a processing graph. -Linear Flows however are always immutable and appending an operation to a Flow always returns a new Flow instance. -This means that you can safely re-use one given Flow in multiple places in a processing graph. In the example below -we prepare a graph that consists of two parallel streams, in which we re-use the same instance of :class:`Flow`, -yet it will properly be materialized as two connections between the corresponding Sources and Sinks: +We have seen examples of such re-use already above: the merge and broadcast junctions were imported +into the graph using ``builder.graph(...)``, an operation that will make a copy of the blueprint that +is passed to it and return the inlets and outlets of the resulting copy so that they can be wired up. +Another alternative is to pass existing graphs—of any shape—into the factory method that produces a +new graph. The difference between these approaches is that importing using ``b.graph(...)`` ignores the +materialized value of the imported graph while importing via the factory method allows its inclusion; +for more details see :ref:`stream-materialization-scala`. + +In the example below we prepare a graph that consists of two parallel streams, +in which we re-use the same instance of :class:`Flow`, yet it will properly be +materialized as two connections between the corresponding Sources and Sinks: .. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/stream/FlowGraphDocTest.java#flow-graph-reusing-a-flow @@ -73,55 +82,62 @@ yet it will properly be materialized as two connections between the correspondin Constructing and combining Partial Flow Graphs ---------------------------------------------- + Sometimes it is not possible (or needed) to construct the entire computation graph in one place, but instead construct all of its different phases in different places and in the end connect them all into a complete graph and run it. -This can be achieved using :class:`PartialFlowGraph`. The reason of representing it as a different type is that a -:class:`FlowGraph` requires all ports to be connected, and if they are not it will throw an exception at construction -time, which helps to avoid simple wiring errors while working with graphs. A partial flow graph however does not perform -this validation, and allows graphs that are not yet fully connected. +This can be achieved using ``FlowGraph.factory().partial()`` instead of +``FlowGraph.factory().closed()``, which will return a ``Graph`` instead of a +``RunnableFlow``. The reason of representing it as a different type is that a +:class:`RunnableFlow` requires all ports to be connected, and if they are not +it will throw an exception at construction time, which helps to avoid simple +wiring errors while working with graphs. A partial flow graph however allows +you to return the set of yet to be connected ports from the code block that +performs the internal wiring. -A :class:`PartialFlowGraph` is defined as a :class:`FlowGraph` which contains so called "undefined elements", -such as ``UndefinedSink`` or ``UndefinedSource``, which can be reused and plugged into by consumers of that -partial flow graph. Let's imagine we want to provide users with a specialized element that given 3 inputs will pick -the greatest int value of each zipped triple. We'll want to expose 3 input ports (undefined sources) and one output port -(undefined sink). +Let's imagine we want to provide users with a specialized element that given 3 inputs will pick +the greatest int value of each zipped triple. We'll want to expose 3 input ports (unconnected sources) and one output port +(unconnected sink). .. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/stream/StreamPartialFlowGraphDocTest.java#simple-partial-flow-graph -As you can see, first we construct the partial graph that contains all the zipping and comparing of stream -elements, then we import it (all of its nodes and connections) explicitly to the :class:`FlowGraph` instance in which all +As you can see, first we construct the partial graph that describes how to compute the maximum of two input streams, then +we reuse that twice while constructing the partial graph that extends this to three input streams, +then we import it (all of its nodes and connections) explicitly to the :class:`FlowGraph` instance in which all the undefined elements are rewired to real sources and sinks. The graph can then be run and yields the expected result. .. warning:: Please note that a :class:`FlowGraph` is not able to provide compile time type-safety about whether or not all - elements have been properly connected - this validation is performed as a runtime check during the graph's instantiation. + elements have been properly connected—this validation is performed as a runtime check during the graph's instantiation. + + A partial flow graph also verifies that all ports are either connected or part of the returned :class:`Shape`. .. _constructing-sources-sinks-flows-from-partial-graphs-java: Constructing Sources, Sinks and Flows from Partial Graphs --------------------------------------------------------- + Instead of treating a :class:`PartialFlowGraph` as simply a collection of flows and junctions which may not yet all be connected it is sometimes useful to expose such a complex graph as a simpler structure, such as a :class:`Source`, :class:`Sink` or :class:`Flow`. In fact, these concepts can be easily expressed as special cases of a partially connected graph: -* :class:`Source` is a partial flow graph with *exactly one* :class:`UndefinedSink`, -* :class:`Sink` is a partial flow graph with *exactly one* :class:`UndefinedSource`, -* :class:`Flow` is a partial flow graph with *exactly one* :class:`UndefinedSource` and *exactly one* :class:`UndefinedSource`. +* :class:`Source` is a partial flow graph with *exactly one* output, that is it returns a :class:`SourceShape`. +* :class:`Sink` is a partial flow graph with *exactly one* input, that is it returns a :class:`SinkShape`. +* :class:`Flow` is a partial flow graph with *exactly one* input and *exactly one* output, that is it returns a :class:`FlowShape`. Being able to hide complex graphs inside of simple elements such as Sink / Source / Flow enables you to easily create one complex element and from there on treat it as simple compound stage for linear computations. In order to create a Source from a partial flow graph ``Source`` provides a special apply method that takes a function -that must return an ``UndefinedSink``. This undefined sink will become "the sink that must be attached before this Source -can run". Refer to the example below, in which we create a Source that zips together two numbers, to see this graph +that must return an :class:`Outlet`. This unconnected sink will become “the sink that must be attached before this Source +can run”. Refer to the example below, in which we create a Source that zips together two numbers, to see this graph construction in action: .. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/stream/StreamPartialFlowGraphDocTest.java#source-from-partial-flow-graph -Similarly the same can be done for a ``Sink``, in which case the returned value must be an ``UndefinedSource``. +Similarly the same can be done for a ``Sink``, in which case the returned value must be an ``Inlet``. For defining a ``Flow`` we need to expose both an undefined source and sink: .. includecode:: ../../../akka-samples/akka-docs-java-lambda/src/test/java/docs/stream/StreamPartialFlowGraphDocTest.java#flow-from-partial-flow-graph diff --git a/akka-docs-dev/rst/scala/stream-graphs.rst b/akka-docs-dev/rst/scala/stream-graphs.rst index 70a4acda58..b711721ad9 100644 --- a/akka-docs-dev/rst/scala/stream-graphs.rst +++ b/akka-docs-dev/rst/scala/stream-graphs.rst @@ -90,6 +90,7 @@ materialized as two connections between the corresponding Sources and Sinks: Constructing and combining Partial Flow Graphs ---------------------------------------------- + Sometimes it is not possible (or needed) to construct the entire computation graph in one place, but instead construct all of its different phases in different places and in the end connect them all into a complete graph and run it. @@ -116,7 +117,7 @@ the undefined elements are rewired to real sources and sinks. The graph can then .. warning:: Please note that a :class:`FlowGraph` is not able to provide compile time type-safety about whether or not all - elements have been properly connected - this validation is performed as a runtime check during the graph's instantiation. + elements have been properly connected—this validation is performed as a runtime check during the graph's instantiation. A partial flow graph also verifies that all ports are either connected or part of the returned :class:`Shape`. @@ -124,6 +125,7 @@ the undefined elements are rewired to real sources and sinks. The graph can then Constructing Sources, Sinks and Flows from Partial Graphs --------------------------------------------------------- + Instead of treating a partial flow graph as simply a collection of flows and junctions which may not yet all be connected it is sometimes useful to expose such a complex graph as a simpler structure, such as a :class:`Source`, :class:`Sink` or :class:`Flow`.