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
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
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
method, what makes
this reference not accessible inside body. It can be non static for your convenience, just don't access
One might argue that it should be:
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
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:
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
Unfortunately there is no native support of currying in Java so curried form declaration:
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.
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,
If we have some
BiFunction we can curry it and pass to
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:
we took available function
sumaccepting two arguments
transformed it to different function by currying it, so we created
applied our function with value
and finally assigned produced
Written code is as terse as
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
like in this code:
You saw the examples of functions transforming other functions like
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
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:
- 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
syntax. Java compiler noticed, that our
parse static method signature matched expected first argument type of
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
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:
- 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
f(g(h(X x))) , where
x is argument of type
X, we can use
andThen higher order function like
h function's result becomes
g function's argument, and
g function's result becomes
We can apply multiple functions in opposite direction using
compose like this:
h(g(f(X x))). Corresponding code would be:
f function's result becomes
g function's argument, and
g function's result becomes
These are our task's functions combined using
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
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.
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
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
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
compose() methods are
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
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 -
printMessage functions. Let's refactor it to not depend on external variables.
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
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
Playerin 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
andThen() default method.
I will leave the answer for question: Why there is no
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
In next post you will read why it's worth to learn functional programming .