Introduction to Pragmatic Functional Java

java architecture October 29, 2021

Sergey Yevtushenko has written a number of articles recently on DZone, largely around something he calls “Pragmatic Functional Java.”

He says Pragmatic Functional Java focuses on two key rules:

  • Avoid null as much as possible.
  • No business exceptions.

The first thoughts that come up with these is a concern that he’s asserting reliance on Optional<> - and to a degree, he is, although it’s an Option<> instead. The focus is still on avoiding Java’s lack of non-null references, with the results being that you have stream-compatible references (no null references) and no need to check for null, which is less cognitive overhead for developers.

The lack of business exceptions is justified with the following:

PFJ uses exceptions only to represent cases of fatal, unrecoverable (technical) failures. Such an exception might be intercepted only for purposes of logging and/or graceful shutdown of the application. All other exceptions and their interception are discouraged and avoided as much as possible.

Business exceptions are another case of Special States. For propagation and handling of business-level errors, PFJ uses the Result<T> container. Again, this covers all cases when errors may appear - return values, input parameters, or fields. Practice shows that fields rarely (if ever) need to use this container.

There are no justified cases when business-level exceptions can be used. Interfacing with existing Java libraries and legacy code performed via dedicated wrapping methods. The Result<T> container contains an implementation of these wrapping methods.

The No Business Exceptions rule provides the following advantages:

  • Methods which can return errors are immediately visible in code. No need to read. documentation/check source code/analyze call tree to check which exceptions can be thrown and under which conditions.
  • The compiler enforces proper error handling and propagation. Virtually zero boilerplate for error handling and propagation.
  • Code can be written for happy day scenarios and errors handled at the point where this is most convenient - the original intent of exceptions, which was never actually achieved.
  • Code remains composable, easy to read and reason about, no hidden breaks or unexpected transitions in the execution flow - what you read is what will be executed.

This follows from functional approaches, where the exception mechanism Java uses tends to be suited more for imperative programming; thus, PFJ relies on Result and Option compositions (because an exception condition is a kind of Result, not a breaking flow change.)

It’s a pretty good writeup; functional programming has a lot of strengths (and weaknesses) and the biggest hurdles to migration are in understanding and conversion, because most of the Java ecosystem is not written to be used from a functional programming paradigm.

in architecture functional

Reading time: 2 minutes.