Functional programming is like magic

Like every magic, it's difficult and not for everyone

This is first part of pragmatic functional programming tutorial for Java developers. It's based on my experience taken from lessons of functional programming in Java I give at work. If you read this part already, take a look at the next post: Why functional programming .

I hope you will enjoy it as much as I do teaching.

Functional programming is difficult. It's building blocks are simple but if you are regular Java developer you need to change your perspective and shift paradigm to learn it. It borrows a lot from Category Theory, developed own jargon difficult to grasp at the beginning, uses recursion and sophisticated data structures extensively.

It's not for everyone because people think and solve problems in different ways. For some of us, it's natural to think about objects encapsulating some state and communicating with each other. Some of us prefer to think in terms of some data being passed through chain of transformations to get final desired result. Both ways are Turing equivalent (equally powerful), just like Turing Machine and Lambda Calculus computation models. Ultimately, functional programs are merely nice abstraction on top of fast Turing machines.

So what's the point? Is it worth to learn at all? Yes, I believe it is. It adds several superpowers to our programming toolkit and I will show you what these powers are and how to use them.

But what is functional programming?

This is not an easy question and answer usually depends on whom you ask. Here below you find basic concepts and techniques description.

Program with Pure Functions

This sounds reasonable, functional programming must have something to do with functions, right?

The very first step in learning functional programming is to model the problem solution in terms of pure functions.

Instead of mutating objects like here:

Use pure functions to provide desired answers without object mutations:

Here you saw first code examples and might miss private or public modifiers. I did it by purpose to make code shorter. I will often omit import declarations , getters and setters, generic wildcards declarations (<? super A> and <? extends A>) for the same reason. Please read the code carefully as I will put many explanations there

What makes function pure?

  • Pure function takes some value and returns some value only
  • Pure function is side effects free
  • Pure function depends only on it's arguments
  • Pure function returns the same result if applied to the same arguments, always

Please remember, we say apply function to arguments and not apply arguments to function.

This is nice function, beautiful in it's purity and simplicity. It does only one thing. It takes Integer type argument and returns new Integer number. It doesn't change it's arguments. Actually it can't because java.lang.Integer is immutable. It is side effects free, meaning that no external state is modified, no external variables are assigned. It reads no external variable, all it needs are function's arguments. It is static method, what makes this reference not accessible inside body. It can be non static for your convenience, just don't access this in body.

One might argue that it should be:

The final modifier used in argument protects functions's code from assigning argument reference with new value:

But I'm personally not very strict in using final modifiers in arguments declaration due to it's verbosity. Just keep your functions small and never ever use arguments reassignments, or use final modifiers and let Java compiler protect you from such smelly practices.

There are very important consequences of function purity. To understand what function is doing, you only need to see it's body. To test it, you only need to invoke it and assert result. There are no surprises, no looking around your code to check whether external variable used by your function is modified by external code. And last but not least Referential Transparency - concept I will explain in next parts.

Whichever programming language you use, just follow pure functions rules to start programming functional way.

Higher order functions

Higher order functions is very simple yet powerful concept. It's just function taking another function as argument or function returning function.

hasAge is a higher order function because it accepts java.util.function.Predicate<Integer> as it's argument. Predicate is a generic function taking given type argument and returning boolean.

If you'd ask me if some programming language supports functional programming style, my answer would depend on it's capability of treating functions as first class citizen:

  • can function be passed as an argument to other function
  • can function be returned from function
  • can function be assigned to variable or constant

If above conditions are met, you can program functional way. This is functional programming magic. You can build powerful abstractions using very simple concept of higher order functions.

Currying and Partial Application

Currying and partial application are closely related and extremely useful techniques. We use them to manipulate existing functions and construct new functions from existing functions. It's very common and handy that we create new functions from available building blocks. Currying and partial application are somehow similar to Design Patterns in Object Oriented Programming. In OOP primitives like inheritance, polymorphism and composition are used to construct higher level abstractions. In OOP we use Design Patterns and in FP we use higher order functions, currying and partial application to construct our code abstractions.

Let's look at currying first. The name of this technique is taken from Haskel Curry, American mathematician. The idea is based on the fact:

Every pure function accepting multiple arguments can be transformed to higher order function taking single argument and returning function taking single argument and returning function taking single argument and ... so on, depending on arguments number of function we curry.

Actually, in some programming languages like Haskel we define our functions in curried form by default.

This is simple example of currying:

This a -> b -> = a + b in line 8 is just lambda expression equivalent to Function<A, Function<B, C>>. We can nest any number of -> in lambdas, this is perfectly legitimate syntax.

Unfortunately there is no native support of currying in Java so curried form declaration: Function<Integer, Function<Integer, Integer>> looks weird and is difficult to read. Fortunately there are some mitigation techniques I'll show you later. Despite the lack of currying support, it's easy to write generic function transforming any BiFunction to curried form.

Our curry function is the nice example of higher order function. It takes function as an argument and returns function. You deserve warning now. This is how functional programming code often looks. Please try to understand what's going on in above and following examples and I promise, it will pay off in the near future.

If you wonder how currying might be useful, let me show you one example. Many core Java 8 functions expect Function as it's only argument, like map in Optional or Stream.

If we have some BiFunction we can curry it and pass to Stream.map() or Optional.map() functions without wrapping it with some private function or using lambda expression.

It's time to tackle partial application, but it's too late now. We already covered it. What we did in previous example was:

  1. we took available function sum accepting two arguments
  2. transformed it to different function by currying it, so we created Function<Integer,Function<Integer,Integer>>
  3. applied our function with value 1
  4. and finally assigned produced Function<Integer,Integer> to constant

Written code is as terse as curry(sum).apply(1).

Partial application is the process of transforming multiple arguments function by applying it to less arguments then expected,producing function with arguments number decreased by arguments count we provided.

Lets abstract partial application of any BiFunction into generic function:

I wrote that currying and partial application are closely related because we can write partial function using curry function like in this code:

You saw the examples of functions transforming other functions like curry or partial and can feel a bit confused. Let me give you some exercises to let you practice and get better intuition in manipulating functions.

Write function transforming any two arguments function into two arguments function with arguments reversed. Show answer

Write function currying any two arguments function from the right side. Show answer

Write function currying any two arguments function from the right using reverse function. Show answer

Functions composition

Well, here comes my favorite feature of functions. They do compose really well.

And how do we solve problems? We decompose bigger problems into smaller problems. If the smaller problems are still too big, we decompose them further, and so on. Finally, we write code that solves all the small problems. And then comes the essence of programming: we compose those pieces of code to create solutions to larger problems. Decomposition wouldn’t make sense if we weren’t able to put the pieces back together.

This process of hierarchical decomposition and recomposition is not imposed on us by computers. It reflects the limitations of the human mind. Our brains can only deal with a small number of concepts at a time.

Category Theory for Programmers, Bartosz Milewski

I couldn't agree more with above statements. We do decompose and compose objects using object oriented languages or procedures when procedural languages come to play. Ability to compose is the very nature of pure functions, they just accept value and return value, so we can take any number of functions and chain them in the way, that the result of previous function becomes an input of next function. Just like in following example:

I agree, it's not very useful. Lets look at more realistic example. We have simple task:

Write function accepting user provided string and convert it to percent format if possible. In case of non-number input, just return 0 %.

This is our first attempt:

It looks fine at first glance, but there are some issues in it. It does more than one thing:

  • parses String to BigDecimal
  • multiplies result by 100
  • handle errors

All those actions are hidden inside function body and can't be reused or tested separately. Let's decompose it into smaller chunks and compose back to create our final function.

This is first time we used Java 8 feature: method references. In line 32 we used Composition2::parse syntax. Java compiler noticed, that our parse static method signature matched expected first argument type of applySafe and generated valid type from given static method reference: Composition2::parse == Function<String, BigDecimal>

Well, we have small functions but composing them is the real pain, isn't it? What can we do about this? We can use Java 8 java.util.function.Function interface features and partial application to make them easier to use.

I particularly like the expressiveness of our functions application in last lines. It's like some story told:

  • apply parse safely
  • then multiply by 100
  • then append ' %' string
  • call with '0.5' argument

Let me explain the magic happening in last lines. We use andThen default method of java.util.function.Function interface. This is commented piece of this interface source code:

Whenever we need to apply multiple functions (let's say three functions named f, g, h) one after another like in this pseudocode f(g(h(X x))) , where x is argument of type X, we can use andThen higher order function like here: h.andThen(g).andThen(f).apply(x).

h function's result becomes g function's argument, and g function's result becomes f function's argument.

We can apply multiple functions in opposite direction using compose like this: h(g(f(X x))). Corresponding code would be: h.compose(g).compose(f).apply(x).

f function's result becomes g function's argument, and g function's result becomes h function's argument.

These are our task's functions combined using compose:

Let's get back to our task. Although the way we call our functions looks better (of course only if we get used to this notation), we had to write much more code compared to first version. Fortunately, there is the light ...

We didn't have to create our composing functions at the expense of readability. You might ask which way to go then? I would argue that having small generic functions that can be reused to compose other functions is the good approach. There are plenty of reusable functions available in Java Core API or external libraries, they are thoroughly tested and can save a lot of our time. Such generic functions are called combinators and libraries containing them combinator libraries.

Let's practice little bit and solve some simple tasks using only our combinators like partial or applySafe, functions from Java Core API and from Apache Commons Lang library.

Extend our percent parsing function to trim input string before application. Show answer

Extend our trimming percent parsing function to add two fraction digits. Show answer

Although we have limited number of our programming tools, just higher order functions, partial application and composition, the amount of possible combinations of ways we can structure our code is huge. The only limitation is our imagination ... and Java compiler of course.

Identity

Can you imagine math without concept of zero? I believe you can't. Zero is the specialization of neutral element. When neutral element is used in some operation, final result is not affected. Like zero under number addition x + 0 = x or one under multiplication x * 1 = x. The exact type and value of neutral element depends on the context. It can be number in arithmetic operations or function in functions composition. It may seem useless and unnecessary, but there are multiple possible usages of identity element I will write about in next post. This is identity function in Java 8:

Identity function does nothing more than returning it's argument. When used in functions composition it leaves composed function unchanged:

f.andThen(Function.identity()) == f and f.compose(Function.identity()) == f

And here comes code example with Function.identity():

Let's try to optimize our trimming parser naively just to present Function.identity() use. We want to extend our parser to do some cleanup before actual parsing.

This trivial example shows the way we can use Function.identity() to do nothing under some circumstances. And here below is simple trick we can use to compose functions when our starting function is just static method, so no andThen() or compose() methods are available.

Side effects separation and explicit state mutation

Pure functions here, pure functions there, pure functions everywhere. What about writing to file or socket or reading user's input from console? All such side effects are forbidden for pure functions. The way to handle side effects in functional programming is to separate pure code from side effects code. Let's say we write program fragment responsible for casino players verification. We want to print warning if player's name is on dangerous players list or usual message otherwise.

This is impure version:

It's very tempting but I will not complain about the number of issues in this code. It's time for pure version where we will try to separate pure from side effects code.

The solution is to not perform side effect but to return side effect performing function from pure code. In above example, we return java.util.function.Consumer<Player>. This is the special function. It accepts single generic argument but returns void. If function returns nothing, it's sole purpose is to perform side effects.

Actually, there are some subtlies related to meaning of void and types in general. I'll write about it in next posts.

Our goal was to separate side effects from pure code but we failed. Our verifyPlayer function is still impure, because it access constants not included in arguments - printWarning and printMessage functions. Let's refactor it to not depend on external variables.

Our verifyPlayer function is pure now but still not perfect. It's arguments list is long and it performs only very specific logic. What if we would like to choose side effect basing on some other condition? We would have to write another function. Not very reusable right? Let's look at the code again and try to abstract it into more generic form. The very first thing coming into my mind is the if() condition in line 9. It's just Predicate. We can refactor our function to use predicate.

Finally, we can go further:

  • make our player handler selection the higher order function for easier use
  • make it generic as there is nothing specific to our Player in functions's implementation
  • compose additional handler of dangerous players, like Kafka messages sending

We used known techniques to achieve desired result: higher order functions, currying and composition. The only new thing is Consumer<A> composition using andThen() default method.

I will leave the answer for question: Why there is no compose() in java.util.function.Consumer interface? to you dear readers as an exercise.

If you wonder if all this code splitting into small functions is worth the effort, let me give you some task to solve.

Our program grows as there are new requirements given. There are new player's checks added like age verification, VIP players handling along with additional player's handlers. We know that our Product Owner is considering additional checks and handlers. We want to structure our program to easily handle multiple different cases. We decided to keep conditions and handlers in the list to use it in player's verification code. We need to cover now additional cases: request security assistance for VIP players (we have VIP players list) and warn about players younger than 21 years old. Show answer

What's next?

In next post you will read why it's worth to learn functional programming .


We use cookies to ensure you get the best experience on our site.