Skip to main content
Blog

Functional Java by Example | Part 6 – Functions as Parameters

By 1 december 2018april 29th, 2019No Comments

This is part 6 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 part we tried to make our functions as pure possible by moving as much of the side-effects, such as IO, to the outside of the system.

Now we’re going to replace some of our abstractions into functions, to be passed as parameters.

If you came 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.

Collaborators OO-style

Remember how we left things previously?

class FeedHandler {

  Webservice webservice

  List<Doc> handle(List<Doc> changes) {

    changes
      .findAll { doc -> isImportant(doc) }
      .collect { doc ->
        createResource(doc)
        .thenApply { resource ->
          setToProcessed(doc, resource)
        }
        .exceptionally { e ->
          setToFailed(doc, e)
        }
        .get()
      }
  }

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

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

  private static Doc setToProcessed(doc, resource) {
    doc.copyWith(
      status: 'processed',
      apiId: resource.id
    )
  }

  private static Doc setToFailed(doc, e) {
    doc.copyWith(
      status: 'failed',
      error: e.message
    )
  }

}

Above feed handler needs a “web service” to do its work.

Take a look at the following part where a collaborator of type WebService is used to create a resource based on a document:

class FeedHandler {

  Webservice webservice

  List<Doc> handle(List<Doc> changes) {

    changes
      .collect { doc ->
        createResource(doc)
        ...
  }

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

}

Remember, instead of just returning a resource directly, we’ve wrapped it in a CompletableFuture as part of our exception handling mechanism.

What if we wanted something other than a WebService to create a resource?

Well, this is where it gets tricky and easy at the same time — and where a OO-style can conflict a bit with a FP-style.

You see, WebService is a Java interface and defined as follows:

interface Webservice {
  CompletableFuture<Resource> create(Doc doc)
}

This follows the Dependency Inversion Principle (DIP) — as part of the SOLID design principles promoted by Robert C. Martin — which (amongst others) says:

Abstractions should not depend on details. Details should depend on abstractions.

WebService is already an abstraction for any kind of webservice implementation. So the system could have multiple implementations of this interface e.g. a REST implementation and a SOAP implementation:

class RestWebService implements Webservice {
  @Override
  CompletableFuture<Resource> create(Doc doc) {
    // do REST communication
  }
}
class SoapWebService implements Webservice {
  @Override
  CompletableFuture<Resource> create(Doc doc) {
    // do SOAP communication
  }
}

The feed handler does not care about the details — it just wants something which adheres to the contract defined by the WebService interface: there’s a create method which accepts a Doc and returns a CompletableFuture<Resource>.

The FeedHandler class has a property webservice holding the reference to a WebService. Any OO-developer recognizes this style, because it’s very familiar: all the collaborators are present in properties, which are (often) initialized during constructing.

As soon as FeedHandler is constructed, it gets an instance of WebService passed to it – albeit constructor-injection or property-injection, either through DI frameworks or plain-old manual labor.

For brevity I have been omitting the constructor in my code snippets, but as you can see in my testcases I definitely pass all dependencies using the constructor Groovy generates for me under the hood 🙂

Collaborators FP-style

Ok, if we would put on our Functional Hat again, we would need to revisit the way how a WebService gets passed to the feed handler.

The handle method’s signature does not mention anything other than: documents go in, and documents come out.

class FeedHandler {

  ...

  List<Doc> handle(List<Doc> changes) {

    ...
  }


}

I can not assume the same output is returned for the same input — because the method secretly depends on something on the outside: the WebService.

Well, possibly I control the entire creation of the feed handler, including the WebService, but the reference to webservice can change in between method invocations, yielding other results every time handle is using it. Unless I made it immutable or prevent the reference from being updated. I told you it could get tricky 😉

Can we make handle pure, just as we did in previous installments with the isImportant, setToProcessed and setToFailed methods?

In this case we have to pass WebService in as a parameter, just as the list of documents.

We change

class FeedHandler {

  Webservice webservice

  List<Doc> handle(List<Doc> changes) {

    ...
  }

}

into

class FeedHandler {

  List<Doc> handle(List<Doc> changes, Webservice webservice) {

    ...
  }

}

At every invocation of handle we pass in everything it needs: the documents it needs to handle and the webservice it needs to use.

Since this method no longer depends on any properties in the FeedHandler class anymore, we could have made it static at the moment — upgrading it to a class-level method.

Higher-order functions

Effectively our handle method just became a so-called “higher order function”, a function that takes a function or returns a function.

So, back to a question I asked in the beginning: what if we wanted something other than a WebService to create a resource?

It shouldn’t even have to be a webservice right? Maybe we completely want to go bananas and a have a monkey create a resource for us?

class Monkey implements Webservice {
  @Override
  CompletableFuture<Resource> create(Doc doc) {
    // go bananas! But do create resources plz
  }
}

That just looks weird, doesn’t it? The WebService interface is too specific for the abstraction feed handler needs. Anything which creates resources will do, doesn’t it?

A better name would be “ResourceCreator” — so just rename the interface.

Old:

interface Webservice {
  CompletableFuture<Resource> create(Doc doc)
}

New:

interface ResourceCreator {
  CompletableFuture<Resource> create(Doc doc)
}

A ResourceCreator interface with a create method; how fitting! Now anything can implement this interface, and feed handler doesn’t even care whether or not it is a webservice, a monkey or a Hobbit.

The new method signature:

class FeedHandler {

  List<Doc> handle(List<Doc> changes, 
    ResourceCreator creator) {

    ...
  }

}

Perfect abstraction!

Functional abstractions

In Java we call an interface with only one abstract method a functional interface. Our ResourceCreator fits this description; it has a single, abstract method create.

Java’s java.util.function package has numerous of those functional interfaces — and they each have a single, defined purpose:

  • Consumer represents a function that accepts an argument and returns nothing
  • Supplier represents a function that accepts no arguments, just returns a result
  • Function represents a function that accepts one argument and returns a result
  • …and more

What this means is, that we don’t need to define a specific interface, such as ResourceCreator, every time we need a function “to accept one argument and return a result” — Function is already an interface we can leverage!

This is how Function (simplified) in Java 8 looks like:

interface Function<T,R> {
  R apply(T t);
}

And this is how ResourceCreator looks like right now:

interface ResourceCreator {
  CompletableFuture<Resource> create(Doc doc)
}

You see we can completely substitute our ResourceCreator with a Function if we:

  • substitute Doc for type R
  • substitute CompletableFuture<Resource> for type T
  • substitute calling create by the method apply

We can erase the ResourceCreator interface completely!

The new method signature will become:

class FeedHandler {

  List<Doc> handle(List<Doc> changes,
      Function<Doc, CompletableFuture<Resource>> creator) {

    ...
  }

}

What have we achieved?

  • We can pass any function to handle now which takes a single Doc and produces a single CompletableFuture<Resource> — and that’s all the feed handler needs to work properly.
  • As you’ve probably noticed by now that Functional Programming deals a lot with functions. A function can take another function, or could return a function.
  • As of Java 8 we’ve got a whole bunch of functional interfaces, ready to use. Every developer can work with them in a standardized way, so it’s best to see if they fit your use case and API and re-use them wherever possible. Every one of them have generic types (such as T and R) which can be used by you to indicate what goes in and what comes out of a function.

The complete code now looks like this:

class FeedHandler {

  List<Doc> handle(List<Doc> changes,
    Function<Doc, CompletableFuture<Resource>> creator) {

    changes
      .findAll { doc -> isImportant(doc) }
      .collect { doc ->
        creator.apply(doc)
        .thenApply { resource ->
          setToProcessed(doc, resource)
        }
        .exceptionally { e ->
          setToFailed(doc, e)
        }
        .get()
      }
  }

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

  private static Doc setToProcessed(doc, resource) {
    doc.copyWith(
      status: 'processed',
      apiId: resource.id
    )
  }

  private static Doc setToFailed(doc, e) {
    doc.copyWith(
      status: 'failed',
      error: e.message
    )
  }

}

That’s it for now! Next time, we’re going to treat failures a data.

If you have any comments or suggestions, I’d love to hear about them!

This is a cross-post from my personal blog. Follow me on @tvinke if you like what you’re reading or subscribe to my blog on https://tedvinke.wordpress.com.