As far as I know, there are only two kinds of functions: destructive and constructive.
While a constructive function, as the name implies, creates something, destructive destroys something, but not in the way you can think now.
For example, the function
Function<Integer,Integer> f = (x,y) -> x + y
is constructive . How do you need to build something. In the example, you built a tuple (x, y). Constructive functions have a problem, inability to handle infinite arguments. But the worst thing: you cannot just leave the argument open. You can't just say โok, let x: = 1โ and try every y you can try. You have to build every time the whole tuple with x := 1 . Therefore, if you like to see which functions are returned for y := 1, y := 2, y := 3 , you need to write f(1,1) , f(1,2) , f(1,3) .
In Java 8, constructive functions should be handled (most of the time) using method references, because there are not many advantages to using a constructive lambda function. They are a bit like static methods. You can use them, but they have no real state.
Another type is destructive, it requires something and dismantles it as necessary. For example, destructive function
Function<Integer, Function<Integer, Integer>> g = x -> (y -> x + y)
does the same as the constructive function f . Advantages of the destructive function: you can now handle endless arguments, which is especially convenient for threads, and you can just leave the arguments open. Therefore, if you want to know again what the result will look like if x := 1 and y := 1 , y := 2 , y := 3 , you can say h = g(1) and h(1) is the result for y := 1 , h(2) for y := 2 and h(3) for y := 3 .
So you have a fixed state! This is quite dynamic and in most cases what we want from lambda.
Templates like Factory are much simpler if you can just add a function that does your job.
Destructive easily combined. If the type of rights is right, you can simply compose them as you wish. Using this, you can easily identify morphisms that make (with immutable values) testing much easier!
You can do it too with a constructive one, but a destructive composition looks better and looks more like a list or a decorator, and a constructive one looks very much like a tree. And things like rollback with design features are just not nice. You can just save the partial functions of the destructive (dynamic programming), and on the "backtrack" just use the old destructive function. This makes the code much smaller and easier to read. With constructive functions, you more or less remember all the arguments, which can be many.
So, why is there a need for BiFunction , which should be more problematic than why there is no TriFunction ?
Firstly, a lot of time you have only a few values โโ(less than 3), and you only need the result, so a normal destructive function will not be needed at all, a constructive one will be fine. And there are things like monads that really need a constructive function. But beyond that, there really are no good reasons why BiFunction exists BiFunction all. This does not mean that you need to delete it! I fight for my monads until I die!
So, if you have many arguments that you cannot combine into a class of logical containers, and if you need a function to be constructive, use the method reference. Otherwise, try to use the new acquired ability of destructive functions, you may find that you are doing many things with much fewer lines of code.