Railway-oriented programming in Akka Streams

I’ve recently modelled Scott Wlaschin’s ‘railway-oriented programming’ technique ( https://fsharpforfunandprofit.com/rop/ ) using Akka Streams and have a couple of thoughts.

First, a little motivation. In Akka Streams currently error handling is not very fine-grained. If there’s an exception in any stage, it effectively blows up the remainder of the stream. We can set up custom retry logic at any stage, but the element that caused the error in the first place will be dropped. For finer control, currently the advice is to handle exceptions manually with try/Try. E.g., Questions about error handling in Flows

If you’re familiar with ROP already, here’s the gist of it: you can implement it in Akka Streams with Partitions ( https://doc.akka.io/api/akka/current/akka/stream/scaladsl/Partition$.html , and optionally a Merge if you want to feed all errors into a single sink). The key is to wrap the output of a flow in a result type like Try or Either so you can feed it into a Partition and split the successes and failures into separate streams. Here’s an example:

parseJson ~> partitionParseJson.in
// In the Unix tradition, output 0 is success and 1 is failure
partitionParseJson.out(0) ~> successParseJson
partitionParseJson.out(1) ~> failureParseJson

parseJson can e.g. take a String input to an Either[Error, Json] like Circe does. partitionParseJson can look like:

val partitionParseJson = builder.add(Partition[Either[Error, Json]](2, {
  case Right(_) => 0
  case Left(_) => 1
}))

Once they get the partitioned outputs from the previous stage, successParseJson and failureParseJson will want to extract the actual values so that subsequent stages can work with them:

val successParseJson = Flow[Either[Error, Json]].collect {
  case Right(json) => json
}

In this flow we don’t care about Left values because we know it will only get Right values at runtime, and vice-versa for failureParseJson.

The upshot is that we can set up a literal happy path and sad paths using partitions and propagate successes and errors along them.