There’s a lot of confusion about “functions” in Java. Java doesn’t really have them, but an object could represent a function so it kind of does have them. I try to explain the different meanings of “functions” in Java, other languages and in mathematics.
What is a Function?
Functions in C, Pascal, PHP etc.
In many languages anything that returns something (not void) is a function. This is confusing because leaving some return value on the stack after returning has nothing to do with functions in mathematics. It simply means that it doesn’t just do something, it returns the result. But it could still do something, so it can have a side effect. It’s simply a procedure that yields some value, not really a function.
Functions in Mathematics
So basically it’s something that takes some data and returns some result. The data can consist of many values, but it’s always one input, which could be a tuple (a pair, a triplet etc.) of many values. The result could be a single value, a tuple, or another function. If it returns another function then the input for that could be considered the second argument.
In math nothing has a side effect simply because there’s nothing to have an effect on. A good math teacher would not change anything that is already written on the chalk board. If x is 5 then x is 5, always. It’s not a variable that can suddenly become 6. It doesn’t just have a value of 5, it really is 5. You can’t add elements to a set. You can only define a new set that contains all elements of another set, plus some more element. So functions do not have side effects because there couldn’t be any.
Functions in Java
For a long time it was clear that Java does not have functions, because subroutines are called methods. Since Java 8 we have an interface called
Function. Any instance of that is obviously to be considered a function. The compiler won’t check if it has side effects, so it’s not really different from a method.
In general, methods and constructors are not functions. So Java doesn’t have functions just as it doesn’t have constants. However,
Math.PI actually is a constant (because π is a constant) and
Math.sqrt(double) is a function (as defined in mathematics). But the JVM wouldn’t know about that. So a method could very well be a function, but the JVM doesn’t know because there is no keyword to mark a method as a function. There is @FunctionalInterface, but that is used for non-functions too (methods with side effects). This interface only informs the programmers that it has only one single abstract method.
If something is a function then this should be mentioned in the documentation. It is important to know if a method has side effects or not. Some methods have side effects but match the signature of
Collection::add can be assigned to
Function<E, Boolean>, but it alters a collection:
List<Integer> list = java.util.Arrays.asList(1, 2, 3);
Function<Integer, Boolean> add = list::add; // not a function!
In some situations a function must be side-effect-free. The method
getAndUpdate is a good example:
javase 8 → api → AtomicReference → getAndUpdate
Functions in Functional Languages
Since the compiler of a functional language knows that a function is side-effect-free, it can handle them as such. If the same input is applied twice it doesn’t have to calculate the result twice. If it isn’t even clear whether the result is needed it can delay the execution (lazy), while in Java all expressions must be evaluated before the result can be passed as an argument (eager).
So a method and a function are really quite different. Scala is based on Java technology and uses some concepts of functional programming. Haskell on the other hand is purely functional, and has no methods/side effects.
A function in a purely functional language never really has more than one parameter. But it can have an arity other than 1. It could take an tuple or return a function that takes more parameters (see the following example).
All operators are functions. Addition looks like this:
+ : integer -> integer -> integer
This translates to: Take some integer, return a function that takes another integer and return an integer. Another form of addition is:
+ : (integer, integer) -> integer
Here it takes a pair of two integers and returns one integer. The input is any element from the set of all pairs of integers.
Both are virtually the same. In functional languages, like Haskell, you often see the first.
In Java you usually only see something that works like the second form (
IntBinaryOperator), but there isn’t really a pair involved. You actually have two parameters and the syntax only makes it look like a pair. You could define
Function<Pair<Integer, Integer>, Integer>, but that isn’t very common and Java lacks a predefined type
If you want to make sure something really is a function:
- Does the same input yield the same output?
Could you cache the results or is there a possibility that f(x) at first returns y and the next time z? A function is something that doesn’t use any other data than the parameters and therefore the application of the same values always results in the same return value.
- Is it side-effect-free?
Since a function only works with the values of its parameters it can’t change anything. Everything should be the same after some function was executed. The only thing that happens is that some result was returned. However, if the function uses an internal cache and changes are not visible from outside, it can still be considered side-effect-free.
- Does it check for illegal arguments?
A function that takes integers doesn’t have to work for all integer values. It could only work for positive numbers. But illegal parameter values should trigger an IllegalArgumentException (or NullPointerException). If not it could still be considered to be a function, but to get a robust application you should really check the arguments.
If it differs from any point you should mention it in the documentation of the “functional” object.