Reputation: 37106
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
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
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.
IntFunction<IntUnaryOperator> curriedAdd = (a) -> {
return (b) -> {
return a + b;
};
};
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
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
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
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
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
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