gstackoverflow
gstackoverflow

Reputation: 37106

What does lambda with 2 arrows mean in Java 8?

I have read several Java 8 tutorials before.

Right now I encountered following topic: Does java support Currying?

Here, I see following code:

IntFunction<IntUnaryOperator> curriedAdd = a -> b -> a + b;
System.out.println(curriedAdd.apply(1).applyAsInt(12));

I understand that this example sum 2 elements but I cannot understand the construction:

a -> b -> a + b;

According to the left part of expression, this row should implement following function:

R apply(int value); 

Before this, I only met lambdas only with one arrow.

Upvotes: 123

Views: 17052

Answers (7)

clipper1995
clipper1995

Reputation: 49

After reading the answers, I felt the need to add examples using user interfaces. These examples can improve the understanding of currying.

interface SetLeft {
    SetRight addLeft(int left);
}

interface SetRight {
    int addRight(int right);
}
public static void main(String[] args) {

        SetLeft lambdaSum = l -> r -> l + r;
        int summa = lambdaSum
                .addLeft(2)
                .addRight(4);
        System.out.println(summa);

        //This is the same expression, expanded into anonymous classes
        SetLeft lambdaDetail = new SetLeft() {
            @Override
            public SetRight addLeft(int l) {
                return new SetRight() {
                    @Override
                    public int addRight(int r) {
                        return l + r;
                    }
                };
            }
        };

        summa = lambdaDetail
                .addLeft(2)
                .addRight(4);
        System.out.println(summa);
    }

And another example for using user interfaces and the Builder pattern:

@FunctionalInterface
interface AddPersonId {
    AddPersonName withId(Integer id);
}

@FunctionalInterface
interface AddPersonName {
    AddPersonDoB withName(String name);
}

@FunctionalInterface
interface AddPersonDoB {
    Person withDoB(LocalDate dob);
}

class Person {
    private Integer id;
    private String name;
    private LocalDate datOfBirth;

    public Person(Integer id, String name, LocalDate datOfBirth) {
        this.id = id;
        this.name = name;
        this.datOfBirth = datOfBirth;
    }

    static AddPersonId build() {
        
        return id 
        -> name 
        -> dob 
        -> new Person(id, name, dob);
    }
}


public class Main {
    public static void main(String[] args) {
        Person person = Person.build()
                .withId(1)
                .withName("Bob")
                .withDoB(LocalDate.parse("1985-01-01"));
    }
}

Note that in the chain of interface methods, the first one returns the next interface type, and the last one returns an object.

Upvotes: 0

Adam
Adam

Reputation: 36733

If you express this as non-shorthand lambda syntax or pre-lambda Java anonymous class syntax it is clearer what is happening...

The original question. Why are two arrows? Simple, there are two functions being defined... The first function is a function-defining-function, the second is the result of that function, which also happens to be function. Each requires an -> operator to define it.

Non-shorthand

IntFunction<IntUnaryOperator> curriedAdd = (a) -> {
    return (b) -> {
        return a + b;
    };
};

Pre-Lambda before Java 8

IntFunction<IntUnaryOperator> curriedAdd = new IntFunction<IntUnaryOperator>() {
    @Override
    public IntUnaryOperator apply(final int value) {
        IntUnaryOperator op = new IntUnaryOperator() {
            @Override
            public int applyAsInt(int operand) {
                return operand + value;
            }
        };
        return op;
    }
};

Upvotes: 120

dhke
dhke

Reputation: 15398

If you look at IntFunction it might become clearer: IntFunction<R> is a FunctionalInterface. It represents a function that takes an int and returns a value of type R.

In this case, the return type R is also a FunctionalInterface, namely an IntUnaryOperator. So the first (outer) function itself returns a function.

In this case: When applied to an int, curriedAdd is supposed to return a function that again takes an int (and returns again int, because that's what IntUnaryOperator does).

In functional programming it is common to write the type of a function as param -> return_value and you see exactly that here. So the type of curriedAdd is int -> int -> int (or int -> (int -> int) if you like that better).

Java 8's lambda syntax goes along with this. To define such a function, you write

a -> b -> a + b

which is very much similar to actual lambda calculus:

λa λb a + b

λb a + b is a function that takes a single parameter b and returns a value (the sum). λa λb a + b is a function that accepts a single parameter a and returns another function of a single parameter. λa λb a + b returns λb a + b with a set to the parameter value.

Upvotes: 8

Tunaki
Tunaki

Reputation: 137249

Let's rewrite that lambda expression with parentheses to make it more clear:

IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));

So we are declaring a function taking an int which returns a Function. More specifically, the function returned takes an int and returns an int (the sum of the two elements): this can be represented as an IntUnaryOperator.

Therefore, curriedAdd is a function taking an int and returning an IntUnaryOperator, so it can be represented as IntFunction<IntUnaryOperator>.

Upvotes: 24

a better oliver
a better oliver

Reputation: 26848

It's two lambda expressions.

IntFunction<IntUnaryOperator> curriedAdd = 
  a -> { //this is for the fixed value
    return b -> { //this is for the add operation
      return a + b;
    };
  }

IntUnaryOperator addTwo = curriedAdd.apply(2);
System.out.println(addTwo.applyAsInt(12)); //prints 14

Upvotes: 10

Tagir Valeev
Tagir Valeev

Reputation: 100289

Adding parentheses may make this more clear:

IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));

Or probably intermediate variable may help:

IntFunction<IntUnaryOperator> curriedAdd = a -> {
    IntUnaryOperator op = b -> a + b;
    return op;
};

Upvotes: 30

Alexis C.
Alexis C.

Reputation: 93872

An IntFunction<R> is a function int -> R. An IntUnaryOperator is a function int -> int.

Thus an IntFunction<IntUnaryOperator> is a function that takes an int as parameter and return a function that takes an int as parameter and return an int.

a -> b -> a + b;
^    |         |
|     ---------
|         ^
|         |
|         The IntUnaryOperator (that takes an int, b) and return an int (the sum of a and b)
|
The parameter you give to the IntFunction

Maybe it is more clear if you use anonymous classes to "decompose" the lambda:

IntFunction<IntUnaryOperator> add = new IntFunction<IntUnaryOperator>() {
    @Override
    public IntUnaryOperator apply(int a) {
        return new IntUnaryOperator() {
            @Override
            public int applyAsInt(int b) {
                return a + b;
            }
        };
    }
};

Upvotes: 49

Related Questions