In Akka streams what does Mat in Source[Out, Mat] or Sink[In, Mat] represent. When will it actually be used?
Akka streams consist of 3 major components in it – Source, Flow, Sink – and any non-cyclical stream consist of at least 2 components Source, Sink and any number of Flow element. Here we can say Source and Sink are the special cases of Flow. Source – this is the Source of data. It has exactly one output.
The starting point is called Source and can be a collection, an iterator, a block of code which is evaluated repeatedly or a org.reactivestreams.Publisher. A flow with an attached input and open output is also a Source. A flow may also be defined without an attached input or output and that is then a Flow.
Akka Streams components work with a demand-based protocol. In other words, data flows through the graph as a response to demand from receivers. Producers then comply and send more elements downstream. A second (transparent) protocol kicks in when production of elements is faster than demand.
Actor Materializer Lifecycle. The Materializer is a component that is responsible for turning the stream blueprint into a running stream and emitting the “materialized value”.
The Mat
type parameter represents the type of the materialized value of this stream.
Remember that in Akka Source
, Flow
, Sink
(well, all graphs) are just blueprints - they do not do any processing by themselves, they only describe how the stream should be constructed. The process of turning these blueprints into a working stream with live data is called materialization.
The core method for materializing a stream is called run()
, and it is defined in the RunnableGraph
class. All other methods to run a stream (e.g. runWith
on a Sink
or Source
) eventually delegate to this method. You can see that this method returns Mat
. That is, materializing a stream yields a materialized value.
For example, there is a sink which combines all the values in a stream into a single value, it is constructed with Sink.fold
. But how do you get this value? Since the stream is running asynchronously, a natural type for this value would be Future[T]
, where T
is the type of the fold accumulator. Turns out, Sink.fold
returns Sink[In, Future[T]]
, that is, this Future[T]
is its materialized value, therefore, when you materialize it, you get an instance of Future[T]
which you can then use in your own code for further processing: it will complete with a value if the stream completes correctly and it will complete with a failure if the stream has terminated with an exception.
Each part of the graph you construct by combining sinks, sources and flows (and other kinds of graphs) may potentially have an associated materialized value. For example, materialized value of Source.queue
is a queue which you can use to push elements to the stream once it is materialized, and materialized value of Sink.actorSubscriber
is an ActorRef
which you can use to interact with the actor (which is created by the materializer when the stream is materialized). On the other hand, there is Flow.map
which is a flow with no meaningful materialized value (there is nothing you can externally control when you only apply a pure function to a stream), therefore its materialized value is NotUsed
, which is essentially Unit
.
Naturally, it is possible for different parts of stream contain their own materialized value. For example, nothing prevents you from combining Source.queue
and Sink.fold
. But RunnableGraph.run()
can only return one materialized value. To overcome this, there are usually two variants of combining methods on Sink
s, Flow
s, and other graphs, usually called like method
and methodMat
, e.g. to
and toMat
. The second variant allows you to choose how to combine materialized values of the streams you are joining. For example, you can put them into a tuple to get them both:
val (queue, future) = Source.queue[Int](10, OverflowStrategy.fail)
.map(x => x + 10)
.toMat(Sink.fold(0)(_ + _))(Keep.both)
.run()
Default combination methods (without the Mat
suffix) usually choose either the left or the right materialized value, depending on what would be the most natural thing to do for this particular kind of stream. The Keep
object contains convenience methods which return either left, right or both arguments, specifically for the purpose of using them as the last argument for *Mat
methods, but nothing prevents you from writing your own combining function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With