What I Learned at Work this Week: map vs flatMap with Either in Java

Mike Diaz
4 min readNov 19, 2023

--

Photo by Andrew Neel: https://www.pexels.com/photo/assorted-map-pieces-2859169/

When I write Java at work, I’m frequently working with vavr’s Either class. And when I’m working with vavr’s Either class, I’m confused 100% of the time. Now, I know what you’re thinking — “how could writing methods where the results could be two distinct types ever be problematic in a language that’s obsessed with knowing every object’s type?” Believe it or not, it has caused problems for me.

One problem I ran into this week is that when a method returns an Either, we have to parse the return with some sort of map before we actually have access to the data we’re looking for. Before we dig any deeper, let’s make sure we’re on the same page about Either.

Either

Here’s some helpful info from the docs:

Either represents a value of two possible types. An Either is either a Either.Left or a Either.Right.

Example: A compute() function, which results either in an Integer value (in the case of success) or in an error message of type String (in the case of failure). By convention the success case is Right and the failure is Left.

My Either methods are usually connected to an API of some sort — if my call to the API fails, I won’t get the object I’m looking for, but an error. So my Either.Left is always an Exception, and my Either.Right is the expected/happy path outcome. Here’s a simplified example of something I wrote recently:

Either<Problem, DesiredResult> uploadFile(
String fileName, String fileLocation) {
if (Strings.isEmpty(fileName) || Strings.isEmpty(fileLocation) {
return Either.left(Problem.invalidArgument("Empty argument");
}
return Either.right(s3Service.putObject(bucketName, fileName, fileLocation));
}

I’m asking you to use your imagination here a little bit, as you likely don’t know exactly what the Problem class looks like, and you haven’t seen me define the s3Service object. But hopefully the logic here is simple enough to understand the principle: if there is a reason to believe we’re not on our happy path, we’re going to throw a Problem. One other potentially confusing thing here is Either.Left vs Either.left. I believe the distinction is that the former is the return’s type or class and the latter is the method we actually use to cast the result as that class.

Handling the results of this method is where we have to use map or flatMap.

map and flatMap

The uploadFile method is connected to logs and a user interface, so we want to propagate whether it executed successfully or not. Here’s a snippet from the method that invokes uploadFile:

return uploadFile(config.fileName, config.fileLocation)
.flatMap(putObjectResult -> updateStatus(config, putObjectResult);

If we want to access the return value of an Either, we *must* use map or flatMap. If we try to access it directly, we’ll get unexpected results. For example, if my Either.right were a String, I couldn’t do this:

Either<Problem, String> hypotheticalEitherMethod(String testParam) {
if (testParam.equals("badArg") {
return Either.left("This is a bad result.");
}
return Either.right("This is a good result");

// THIS WON'T WORK
return hypotheticalEitherMethod("Happy Path").equals("This is a good result");

Instead, we have to make sure to do this:

return hypotheticalEitherMethod("Happy Path")
.map(result -> result.equals("This is a good result"));

As for the original question in this post, flatMap gives us even more power when it comes to examining our results. map's limitation is that it can only parse results on their top layer. So if the result contains another Either, or an Optional, or even a Stream, we’d have to chain on another map to get in there. A flatMap, however, will allow us to drill down to the bottom and find what we need. This is most relevant to me when I’m writing Either methods that call Either methods. At the very top of the program, I’ll likely need a flatMap to access what I did at the end of the chain.

I’m still not sure I’d pass an interview question on map vs flatMap at this point, but I do have a feeling I’ve been using flatMap more than I need to in my code. I’m looking forward to doing an experiment and seeing what results I get if I replace some of those with maps. As usual, I encourage you to read some of the resources I consulted, as they provide more examples and get deeper into the subject matter, so they’ll likely explain things better.

Sources

--

--

Mike Diaz
Mike Diaz

Responses (2)