Core Java

Functional Java by Example | Part 3 – Don’t Use Exceptions to Control Flow

This is part 3 of the series called “Functional Java by Example”.

The example I’m evolving in each part of the series is some kind of “feed handler” which processes documents. In previous parts I started with some original code and applied some refactorings to describe “what” instead of “how”.

In order to help the code going forward, we need to get rid of the good ol’ java.lang.Exception. (disclaimer: we can’t actually get rid of it) That’s where this part comes in.

If you came here for the first time, it’s best to start reading from the beginning. It helps to understand where we started and how we moved forward throughout the series.

These are all the parts:

I will update the links as each article is published. If you are reading this article through content syndication please check the original articles on my blog.

Each time also the code is pushed to this GitHub project.

Getting up to speed about Exceptions

Our java.lang.Exception has been around since Java 1.0 – and has basically been our friend in good times and nemesis at other times.

There’s not much to talk about them, but if you want to read up on a few sources, here are my favorites:

You on Java 8 already? Life became so much better! I… Err…oh, wait.

Ok, seems that there’s no way you can actually do it right.

At least, after reading above list, we’re now completely up-to-speed on the topic ��

Luckily I don’t have to write a blog post any more about what’s been covered for 95% already in above articles, but I’ll focus here on the one Exception we actually have in the code ��

Side effects

Since you’re reading this post, you’re probably interested in why this all has to do with functional programming.

On the road to approaching your code in a more “functional way”, you may have encountered the term “side effect” and that it’s a “bad thing”.

In the real world, a side effect is something you did not intend to happen, and you might say it’s equivalent to an “exceptional” situation (you would indicate with an exception), but it has a more strict meaning in a Functional Programming context.

The Wikipedia-article about a Side effect says:

Side effect (computer science) In computer science, a function or expression is said to have a side effect if it modifies some state outside its scope or has an observable interaction with its calling functions or the outside world besides returning a value. … In functional programming, side effects are rarely used.

So let’s see how our FeedHandler code currently looks like after the first two articles in this series:

class FeedHandler {

  Webservice webservice
  DocumentDb documentDb

  void handle(List<Doc> changes) {

    changes
      .findAll { doc -> isImportant(doc) }
      .each { doc ->

      try {
        def resource = createResource(doc)
        updateToProcessed(doc, resource)
      } catch (e) {
        updateToFailed(doc, e)
      }
    }
  }

  private Resource createResource(doc) {
    webservice.create(doc)
  }

  private boolean isImportant(doc) {
    doc.type == 'important'
  }

  private void updateToProcessed(doc, resource) {
    doc.apiId = resource.id
    doc.status = 'processed'
    documentDb.update(doc)
  }

  private void updateToFailed(doc, e) {
    doc.status = 'failed'
    doc.error = e.message
    documentDb.update(doc)
  }

}

There’s one place where we try-catch exceptions, and that’s where we loop through the important documents and try to create a “resource” (whatever that is) for it.

try {
  def resource = createResource(doc)
  updateToProcessed(doc, resource)
} catch (e) {
  updateToFailed(doc, e)
}

In code above catch (e) is Groovy shorthand for catch (Exception e).

Yes, that’s the generic java.lang.Exception which we’re catching. Could be any exception, including NPE.

If there’s no exception thrown from the createResource method, we update the document (“doc”) to ‘processed’, else we update it to ‘failed’. BTW, even updateToProcessed can throw an exception too, but for the current discussion I’m actually only interested in a successful resource creation.

So, above code works (I’ve got the unit tests to prove it :-)) but I’m not happy with the try-catch statement as it is now. I’m only interested in successful resource creation, and, silly me, I could only come up with createResource either returning a successful resource or throwing an exception.

Throwing an exception to signal something went wrong, get the hell out of dodge, have caller catch the exception in order to handle it, is why exceptions were invented right? And it’s better than returning null right?

It happens all the time. Take some of our favorite frameworks, such as EntityManager#find from the JPA spec:

Arg! Returns null.

Returns:
the found entity instance or null if the entity does not exist

Wrong example.

Functional Programming encourages side-effect free methods (or: functions), to make the code more understandable and easier to reason about. If a method just accepts certain input and returns the same output every time – which makes it a pure function – all kinds of optimizations can happen under the hood e.g. by the compiler, or caching, parallelisation etc.

We can replace pure functions again by their (calculated) value, which is called referential transparancy.

In previous article, we’ll already extracted some logic into methods of their own, such as isImportant below. Given the same document (with the same type property) as input, we’ll get the same (boolean) output every time.

boolean isImportant(doc) {
  doc.type == 'important'
}

Here there’s no observable side effect, no global variables are mutated, no log file is updated – it’s just stuff in, stuff out.

Thus, I would say that functions which interact with the outside world through our traditional exceptions are rarely used in functional programming.

I want to do better than that. Be better.

Optional to the rescue

As Benji Weber expresses it:

There are different viewpoints on how to use exceptions effectively in Java. Some people like checked exceptions, some argue they are a failed experiment and prefer exclusive use of unchecked exceptions. Others eschew exceptions entirely in favour of passing and returning types like Optional or Maybe.

Ok, let’s try Java 8’s Optional so signal whether a resource can or can not be created.

Let’s change the our webservice interface and createResource method to wrap and return our resource in an Optional:

//private Resource createResource(doc) {
private Optional<Resource> createResource(doc) {
  webservice.create(doc)
}

Let’s change the original try-catch:

try {
  def resource = createResource(doc)
  updateToProcessed(doc, resource)
} catch (e) {
  updateToFailed(doc, e)
}

to map (processing resource) and orElseGet (processing empty optional):

createResource(doc)
  .map { resource ->
    updateToProcessed(doc, resource)
  }
  .orElseGet { /* e -> */
    updateToFailed(doc, e)
  }

Great createResource method: either correct result comes back, or an empty result.

Wait a minute! The exception e we need to pass into updateToFailed is gone: we have an empty Optional instead. We can’t store the reason why it failed — which we do need.

May be an Optional just signals “absence” and is a wrong tool for our purpose here.

Exceptional completion

Without the try-catch and with the map-orElseGet instead, I do like the way the code started to reflect the “flow” of operations more. Unfortunately, using Optional was more appropriate for “getting something” or “getting nothing” (which names like map and orElseGet also suggested) and didn’t give us the opportunity to record a reason for failing.

What’s another way to either get the successful result or get the reason for failing, still approaching our nice way of reading?

A Future. Better yet: a CompletableFuture.

A CompletableFuture (CF) knows how to return a value , in this way it’s similar to an Optional. Usually a CF is used for getting a value set in the future, but that’s not what we want to use it for…

From the Javadoc:

A Future that …, supporting … actions that trigger upon its completion.

Jip, it can signal “exceptional” completion — giving me the opportunity to act upon it.

Let’s change the map and orElseGet:

createResource(doc)
  .map { resource ->
    updateToProcessed(doc, resource)
  }
  .orElseGet { /* e -> */
    updateToFailed(doc, e)
  }

to thenAccept (processing success) and exceptionally (processing failure):

createResource(doc)
  .thenAccept { resource ->
    updateToProcessed(doc, resource)
  }
  .exceptionally { e ->
    updateToFailed(doc, e)
  }

The CompletableFuture#exceptionally method accepts a function with our exception e with the actual reason for failure.

You might think: tomayto, tomahto. First we had try-catch and now we have thenAccept-exceptionally, so what’s the big difference?

Well, we can obviously not get rid of the exceptional situations, but we’re now thinking like a resident of Functionalville would: our methods start to become functions, telling us something goes in and something goes out.

Consider it a small refactoring we need towards part 4, limiting the amount of side effects in our code even more, and part 5.

This is it for now

For reference, here’s the full version of the refactored code.

class FeedHandler {

  Webservice webservice
  DocumentDb documentDb

  void handle(List<Doc> changes) {

    changes
      .findAll { doc -> isImportant(doc) }
      .each { doc ->
        createResource(doc)
        .thenAccept { resource ->
          updateToProcessed(doc, resource)
        }
        .exceptionally { e ->
          updateToFailed(doc, e)
        }
      }
  }

  private CompletableFuture<Resource> createResource(doc) {
    webservice.create(doc)
  }

  private boolean isImportant(doc) {
    doc.type == 'important'
  }

  private void updateToProcessed(doc, resource) {
    doc.apiId = resource.id
    doc.status = 'processed'
    documentDb.update(doc)
  }

  private void updateToFailed(doc, e) {
    doc.status = 'failed'
    doc.error = e.message
    documentDb.update(doc)
  }

}

Published on Java Code Geeks with permission by Ted Vinke, partner at our JCG program. See the original article here: Functional Java by Example | Part 3 – Don’t Use Exceptions to Control Flow

Opinions expressed by Java Code Geeks contributors are their own.

Ted Vinke

Ted is a Java software engineer with a passion for Web development and JVM languages and works for First8, a Java Web development company in the Netherlands.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button