Expressive and short way to write lambda expression at Java

Darr Mirr
10 min readJan 16, 2021

Introduction

What is more expressive and shorter Java function definition?

Function<Integer, Function<Integer, Integer>> sum = x -> y -> x + y;

or

var sum = def((Integer x) -> (Integer y) -> x + y);

In my opinion, the second definition is more expressive and shorter. If you agree with me I recommend to read further.

You can feel free to move to Conclusion part in order to read `def` method implementation.

Problem description

Lambda expression has introduced at Java 8. And It looks like this:

Function<Integer, Integer> square = x -> x * x;

Looks pretty enough, is it? Let’s look for another example:

Function<Integer, Function<Integer, Integer>> sum = x -> y -> x + y;

I just add only one parameter, but local variable type become much bigger! What about three parameters?

Function<Integer, Function<Integer, Function<Integer, Integer>>> sum3 = x -> y -> z -> x + y + z;

I think it looks a little bit ugly. Why do I have to write so many code? “This is Java” — someone could say.

But at real, there are two reason. First, Java is strong type programming language. And second, Java compiler could not infer local variable type only from lambda expression. Therefore, I have to write local variable type explicitly.

How to deal with huge type definition

New keyword “var has introduced at Java 10 in order to simplify type local variable type definition. So, I rewrite square function using keyword “var”:

var square = x -> x * x;

It looks cool, doesn’t it? But Java compiler does not think so and it complains to this line:

Error:(28, 13) java: cannot infer type for local variable square
(lambda expression needs an explicit target-type)

As mention above, Java Compiler could not infer local variable type only from lambda expression. But how to provide target-type for lambda expression?

Let me show example before I answer the question. Imaging that you have to meet someone at airport. But you have never seen this person. You only have a person’s first name and some description of his features. Finally, a person is appeared and he fits to features that you have. Is it a person what you seek? How to verify it? Just ask his full name.

So, local variable name is person’s first name. Lambda body is person’s features. Full name is local variable type and name. Java Compiler is person who have to meet someone at airport. How Java Compiler can ask lambda body’s full name? Method that I use inspired by Identity Function. Identity function is a function that always return the same value that was passed as argument. For this reason, I create utility interface Functions:

public interface Functions {}

Add method to define lambda full name:

public interface Functions {

static <A, R> Function<A, R> def(Function<A, R> function) {
return function;
}
}

This method return function that is passed as method argument. Let’s write some test example:

public interface Main {

static void main(String[] args) {
var sum1 = Functions.def((Integer x) -> x + x);

System.out.println("sum 1 : " + sum1.apply(1));
}
}

Add static import for `Functions.def `method:

import static com.github.darrmirr.define.way1.utils.Functions.def;public interface Main {

static void main(String[] args) {
var sum1 = def((Integer x) -> x + x);

System.out.println("sum 1 : " + sum1.apply(1));
}
}

Run main method and it works:

sum 1 : 2

Personaly, I believe it looks very pretty for function that have one parameter. Let’s look function that have two parameters:

var sum2 = def((Integer x) -> (Integer y) -> x + y);

The main method also is changed:

public interface Main {

static void main(String[] args) {
var sum1 = def((Integer x) -> x + x);
var sum2 = def((Integer x) -> (Integer y) -> x + y);

System.out.println("sum 1 : " + sum1.apply(1));
System.out.println("sum 2 : " + sum2.apply(1).apply(2));
}
}

Error is occurred during compilation:

Error:(9, 23) java: incompatible types: cannot infer type-variable(s) A,R
(argument mismatch; bad return type in lambda expression
java.lang.Object is not a functional interface)

Variable `sum1` have `Function<A, R>` type but `sum2` one have `Function<A, Function<B, R>>`. Therefore, new `def` method is required to add to Functions utility interface.

public interface Functions {

static <A, R> Function<A, R> def(Function<A, R> function) {
return function;
}

static <A, B, R> Function<A, Function<B, R>> def(Function<A, Function<B, R>> function) {
return function;
}

}

Oh! Unliky! Old error is changed by new one:

Error:(13, 50) java: name clash: <A,B,R>def(java.util.function.Function<A,java.util.function.Function<B,R>>) and <A,R>def(java.util.function.Function<A,R>) have the same erasure

Java Compiler clear all generic during compilcation. Therefore, both method have method argument with the same type `Function`. How to deal with this problem? I found out the way — inheritance. I create a new interface for this purpose:

public interface Currying {
}

I call it Currying. Currying is a method to change function with multiply input arguments to chain of functions with only one input argument. I add inner interface that would represent function for two input argument:

public interface Currying {

interface Of2<A, B, R> extends Function<A, Function<B, R>> {}
}

Also, I make changes to Functions interface:

public interface Functions {

static <A, R> Function<A, R> def(Function<A, R> function) {
return function;
}

static <A, B, R> Function<A, Function<B, R>> def(Currying.Of2<A, B, R> function) {
return function;
}
}

The main method is not changed. Therefore, it is time to run code. Here output:

sum 1 : 2
sum 2 : 3

It works! At last, I try to write function that have three input parameters. Here is code snippet:

public interface Main {

static void main(String[] args) {
var sum1 = def((Integer x) -> x + x);
var sum2 = def((Integer x) -> (Integer y) -> x + y);
var sum3 = def((Integer x) -> (Integer y) -> (Integer z) -> x + y + z);

System.out.println("sum 1 : " + sum1.apply(1));
System.out.println("sum 2 : " + sum2.apply(1).apply(2));
System.out.println("sum 3 : " + sum3.apply(1).apply(2).apply(3));
}
}

Error is occurred after compilation:

Error:(10, 23) java: incompatible types: cannot infer type-variable(s) A,B,R
(argument mismatch; bad return type in lambda expression
bad return type in lambda expression
java.lang.Object is not a functional interface)

Early I have faced with such error. And now I know how to fix it. First, I add inner interface to Currying for function that have three parameters:

public interface Currying {

interface Of2<A, B, R> extends Function<A, Function<B, R>> {}
interface Of3<A, B, C, R> extends Function<A, Function<B, Function<C, R>>> {}
}

Then I add new `def` method to Functions utility interface:

public interface Functions {

static <A, R> Function<A, R> def(Function<A, R> function) {
return function;
}

static <A, B, R> Function<A, Function<B, R>> def(Currying.Of2<A, B, R> function) {
return function;
}

static <A, B, C, R> Function<A, Function<B, Function<C, R>>> def(Currying.Of3<A, B, C, R> function) {
return function;
}
}

And now perform code compilation. Oh, no! Another error compilcation is occured:

Error:(10, 20) java: reference to def is ambiguous
both method <A,B,C,R>def(com.github.darrmirr.define.way1.utils.Currying.Of3<A,B,C,R>) in com.github.darrmirr.define.way1.utils.Functions and method <A,B,R>def(com.github.darrmirr.define.way1.utils.Currying.Of2<A,B,R>) in com.github.darrmirr.define.way1.utils.Functions match

Unfortunatly, I could not realize why Java compiler could not resolve method reference. But I find out someting interesting. Recall main method with compilcation error:

public interface Main {

static void main(String[] args) {
var sum1 = def((Integer x) -> x + x);
var sum2 = def((Integer x) -> (Integer y) -> x + y);
var sum3 = def((Integer x) -> (Integer y) -> (Integer z) -> x + y + z);

System.out.println("sum 1 : " + sum1.apply(1));
System.out.println("sum 2 : " + sum2.apply(1).apply(2));
System.out.println("sum 3 : " + sum3.apply(1).apply(2).apply(3));
}
}

As we know, Main interface have complication error due to reference to `def` method is ambiguous for `sum2` and `sum3` local variables. But It is works for `sum1` and `sum2`. Moreover, IDE properly resolve method reference for `sum3`. It seems Java compiler have some restrictions. And this restrictions is overcomed with some trick. Here is code snippet:

public interface Main {

static void main(String[] args) {
var sum1 = def((Integer x) -> x + x);
var sum2 = def((Integer x) -> (Integer y) -> x + y);
var sum3 = def((Integer x) -> (Integer y) -> def((Integer z) -> x + y + z));

System.out.println("sum 1 : " + sum1.apply(1));
System.out.println("sum 2 : " + sum2.apply(1).apply(2));
System.out.println("sum 3 : " + sum3.apply(1).apply(2).apply(3));
}
}

I add `def` method invocation before last function that receiv last argument. It looks strange but it works! There is no compilation error at all! Here is code’s execution output:

sum 1 : 2
sum 2 : 3
sum 3 : 6

Let’s see code execution order. I add some lines in order to print text to console. Here is my code:

public interface Functions {

static <A, R> Function<A, R> def(Function<A, R> function) {
System.out.println("at def of 1");
return function;
}

static <A, B, R> Function<A, Function<B, R>> def(Currying.Of2<A, B, R> function) {
System.out.println("at def of 2");
return function;
}

static <A, B, C, R> Function<A, Function<B, Function<C, R>>> def(Currying.Of3<A, B, C, R> function) {
System.out.println("at def of 3");
return function;
}
}

Main interface:

public interface Main {

static void main(String[] args) {
System.out.print("start init sum1 ");
var sum1 = def((Integer x) -> x + x);

System.out.print("start init sum2 ");
var sum2 = def((Integer x) -> (Integer y) -> x + y);

System.out.print("start init sum3 ");
var sum3 = def((Integer x) -> (Integer y) -> def((Integer z) -> x + y + z));

System.out.println("sum 1 : " + sum1.apply(1));
System.out.println("sum 2 : " + sum2.apply(1).apply(2));
System.out.println("sum 3 : " + sum3.apply(1).apply(2).apply(3));
}
}

And finally, code’s execution output:

start init sum1 at def of 1
start init sum2 at def of 2
start init sum3 at def of 3
sum 1 : 2
sum 2 : 3
at def of 1
sum 3 : 6

Surprisengly, Java compiler correctly resolve method reference for `sum3` local variable. Inner method `def` is invoked at the same time as according function. Maybe Java compiler could not resolve types with generic deeper than two levels. Who knows?

Definition alternatives

Finally, let’s compare:

standard Java function type definition:

Function<Integer, Function<Integer, Function<Integer, Integer>>> sum3 = (Integer x) -> (Integer y) -> (Integer z) -> x + y + z;

and new one:

var sum3 = def((Integer x) -> (Integer y) -> def((Integer z) -> x + y + z));

Personally I believe, the second Java function type definiton is more expressive and shorter the first one. However, I disappoint a little bit about using two `def` methods for currying function with three arguments.

I have some alternatives to `def` method. The first one is write type expliciltly. Here is code:

var sum3 = Functions.<Integer, Integer, Integer, Integer>def((Integer x) -> (Integer y) -> (Integer z) -> x + y + z);

You can write types before method name. Also you can simplify lambda and make code more consice than previous one. Here is code:

var sum3 = Functions.<Integer, Integer, Integer, Integer>def(x -> y -> z -> x + y + z);

The second alternative is using diffrent method name instead of single `def` one. Here is code:

public interface Functions {

static <A, R> Function<A, R> def1(Function<A, R> function) {
return function;
}

static <A, B, R> Function<A, Function<B, R>> def2(Function<A, Function<B, R>> function) {
return function;
}

static <A, B, C, R> Function<A, Function<B, Function<C, R>>> def3(Function<A, Function<B, Function<C, R>>> function) {
return function;
}
}

And Main interface

public interface MainFinal {

static void main(String[] args) {
var sum1 = def1((Integer x) -> x + x);
var sum2 = def2((Integer x) -> (Integer y) -> x + y);
var sum3 = def3((Integer x) -> (Integer y) -> (Integer z) -> x + y + z);

System.out.println("sum 1 : " + sum1.apply(1));
System.out.println("sum 2 : " + sum2.apply(1).apply(2));
System.out.println("sum 3 : " + sum3.apply(1).apply(2).apply(3));
}
}

Conclusion

I find out three alternatives to standard Java function type definition.

  1. Using single `def` method
var sum1 = def((Integer x) -> x + x);
var sum2 = def((Integer x) -> (Integer y) -> x + y);
var sum3 = def((Integer x) -> (Integer y) -> def((Integer z) -> x + y + z));

Functions utility interface:

public interface Functions {

static <A, R> Function<A, R> def(Function<A, R> function) {
return function;
}

static <A, B, R> Function<A, Function<B, R>> def(Currying.Of2<A, B, R> function) {
return function;
}

static <A, B, C, R> Function<A, Function<B, Function<C, R>>> def(Currying.Of3<A, B, C, R> function) {
return function;
}
}

Currying utility interface:

public interface Currying {

interface Of2<A, B, R> extends Function<A, Function<B, R>> {}
interface Of3<A, B, C, R> extends Function<A, Function<B, Function<C, R>>> {}
}

Prons:

  • short local variable type definion compare with standard one
  • perfect for currying function with one and two argument

Cons:

  • multiply `def` method invocation is required if currying function have more than two argument

2. Write types explicitly before method name

var sum1 = def((Integer x) -> x + x);
var sum2 = def((Integer x) -> (Integer y) -> x + y);
var sum3 = Functions.<Integer, Integer, Integer, Integer>def(x -> y -> z -> x + y + z);

Prons:

  • short local variable type definion compare with standard one
  • perfect for currying function with one and two argument
  • using only one method `def` invocation for currying function that have more than two argument

Cons:

  • currying function definition for 3+ arguments is differ than one for 1 or 2 arguments
  • code is hard to read

3. Using different method names instead of single `def` one

var sum1 = def1((Integer x) -> x + x);
var sum2 = def2((Integer x) -> (Integer y) -> x + y);
var sum3 = def3((Integer x) -> (Integer y) -> (Integer z) -> x + y + z);

Functions utility interface:

public interface Functions {

static <A, R> Function<A, R> def1(Function<A, R> function) {
return function;
}

static <A, B, R> Function<A, Function<B, R>> def2(Function<A, Function<B, R>> function) {
return function;
}

static <A, B, C, R> Function<A, Function<B, Function<C, R>>> def3(Function<A, Function<B, Function<C, R>>> function) {
return function;
}
}

Prons:

  • short local variable type definion compare with standard one
  • only one utility interface is required

Cons:

  • `def` method name is not unified

All code examples and Functions utility interface you can find at my repository on github.com

--

--