Akka Streams – What is NotUsed all about

Akka Streams is a powerful implementation on top of the Reactive Streams SPI for non-blocking asynchronous communication with back-pressure on the JVM. This post is not about explaining what this means, nor what Akka Streams does. The purpose of this post is to explain what on earth the NotUsed type in type signatures of Akka Streams is all about which — incidentally — also means explaining a fundamental design aspect of Akka Streams.

To understand the meaning of NotUsed we need to understand that Akka Streams are hybrid beasts that have two different concepts of value: the value of what “flows” in the stream, and the value of what is produced and visible outside of the stream. See, an Akka Stream is only going to run if it is closed, which is to say that it has a beginning and and end (or a Source and a Sink), and therefore nothing outside the Flow can peek into it. That’s fine if all you do is to carry data from point A to point B, but if by any chance you’d like a stream to produce a value that you’d like to look at, you won’t be able to do so since you can’t touch this.

Let’s look at an example (behold my awesome drawing skills):

Now unless you already know Akka Streams, I’m pretty sure I’ve lost you, which is exactly what I wanted so that you can understand what NotUsed means. So we have a Source that will emit “a”, “b” and then “c”. Next we have a Sink that consumes strings and combines them. We combine both of these, creating a RunnableGraph – it is called like this because there’s both a beginning and an ending, the graph is closed, and hence we can run it. What’s probably very confusing at first is the following line:

Here’s the catch: not only do we want our letters to flow from the source to the sink, but we also would like to retrieve the result of what the sink produces (the concatenation of “a”, “b” and “c”). In Akka slang, that’s materialization. And in order to materialize something we need a materializer. So when we say source.toMat(sink) what we really say is “connect source to sink which is going to produce a value out of it that can be accessed from the outside by mere mortals”. “Okay, great!” you say, “but what’s up with this Keep.right ?”. And you will be right to do so (pun intended) because that’s not obvious either.

Here’s a piece of information that will help, especially if you’re native in a Right-To-Left language: in Akka Streams, graphs are flowing from the left to the right.

Akka Streams allows to materialize (or, to keep) either the left or the right value of a stream. In this case, we want to keep the right one (what comes out of the sink), which is why we specify Keep.Right. You could also want to keep what’s on the left-hand side (in this example, it wouldn’t make very much sense, but in other, more complex cases, it totally does). In fact, you should head over here and read this bit of the Akka documentation.

In our example, the materialized value is going to be a String. But what if we didn’t care about the materialized value? What if we just wanted our flow to run, pushing elements from one place to another? Yes, you guessed it, that’s when we’re using NotUsed as a way of saying “I don’t care about the materialized value”.

Liked this post? Subscribe to the mailing list to get regular updates on similar topics.

One Comment on “Akka Streams – What is NotUsed all about”

  1. Nice post! One question that goes unanswered is why invent NotUsed if there is a seemingly perfectly viable Unit or Void type available. The issue with those is that these types denote that no value is actually returned in many places in their respective language, a void method does not use a return statement. Semantically, materialization must always return something, and if there just is no meaningful thing then we need to make up a value—and we need to use a suitable type that prevents programming errors. In Scala (but also in some Java 8 lambda expressions) it is possible to have the intention of returning a value be silently turned into not returning it if the expected outcome is void. NotUsed was invented to turn these cases into compile-time errors.

Leave a Reply

Your email address will not be published. Required fields are marked *