Materialised value can be produced by both sink and source. One creates a runnable graph by combining a source to a sink. Keep
defines which materialised value to keep when combining
Keep.right
picks materialised value of the sink
Keep.left
picks materialised value of the source
Keep.both
picks both in the form of a tuple
Keep.none
ignores both and picks NotUsed
, ie a marker to indicate that there is no materialised value.
By default, Keep.left
is used in operations via
, to
etc.
Following examples highlight this
Given a Source[Int, String]
and a Sink[Int, Future[Int]]
val source: Source[Int, String] = Source(List(1, 2, 3)).mapMaterializedValue(_ => "Source Mat Value")
val sink: Sink[Int, Future[Int]] = Sink.fold(0)(_ + _)
We can combine a source
and sink
to create a runnable graph with different materialised values.
val left: String = source.to(sink).run() //same as toMat(...)(Keep.left)
val right: Future[Int] = source.toMat(sink)(Keep.right).run()
val both: (String, Future[Int]) = source.toMat(sink)(Keep.both).run()
Now, if we run it and print every materialised value it produces following
left=Source Mat Value
right=Future(Success(6))
both=(Source Mat Value,Future(Success(6)))
Please don't mix up materialised value with processing of elements of the stream.
Consider following fold
stages
val flowFold: Flow[Int, Int, NotUsed] = Flow[Int].fold(0)(_ + _)
val sinkFold: Sink[Int, Future[Int]] = Sink.fold(0)(_ + _)
flowFold
applies fold
function to every element in the stream and pushes one single value representing the result of fold
to downstream. This element can be further processed if needed.
Whereas, sinkFold
is the final stage in a graph and it cannot push elements further downstream. It uses materialised value Future[Int]
to return the fold
result when graph has processed all elements and completed.
if the value of Flow.fold
is 55 should this be the materialised value of the flow instead of NotUsed
.
No, value 55
is not a materialised value. It's pushed as an element to downstream sink.
You can "catch" element 55
in a materialised value with the help of Sink.head
val flow: Flow[Int, Int, NotUsed] = Flow[Int].fold(0)(_ + _)
val alternativeFoldSink: Sink[Int, Future[Int]] = flow.toMat(Sink.head)(Keep.right)
Every stage can produce materialised value then (why) can't Flow.fold
generate materialised value.
Yes, every stage may produce a materialised value. But Flow.fold
is designed not to do so. Most of the Flow
definitions do not provide materialised values. If you want to use materialised value and fold
, I'd suggest to use Sink.fold