¿Qué significa lambda con 2 flechas en Java 8?
java-8 currying (6)
He leído varios tutoriales de Java 8 antes.
En este momento me encontré con el siguiente tema: ¿Java soporta Curry?
Aquí, veo el siguiente código:
IntFunction<IntUnaryOperator> curriedAdd = a -> b -> a + b;
System.out.println(curriedAdd.apply(1).applyAsInt(12));
Entiendo que este ejemplo suma 2 elementos, pero no puedo entender la construcción:
a -> b -> a + b;
De acuerdo con la parte izquierda de la expresión, esta fila debe implementar la siguiente función:
R apply(int value);
Antes de esto, solo conocí lambdas solo con una flecha.
Agregar paréntesis puede aclarar esto:
IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));
O probablemente la variable intermedia puede ayudar:
IntFunction<IntUnaryOperator> curriedAdd = a -> {
IntUnaryOperator op = b -> a + b;
return op;
};
Reescribamos esa expresión lambda con paréntesis para que quede más claro:
IntFunction<IntUnaryOperator> curriedAdd = a -> (b -> (a + b));
Entonces estamos declarando una función tomando un
int
que devuelve una
Function
.
Más específicamente, la función devuelta toma un
int
y devuelve un
int
(la suma de los dos elementos): esto puede representarse como un
IntUnaryOperator
.
Por lo tanto,
curriedAdd
es una función que toma un
int
y devuelve un
IntUnaryOperator
, por lo que puede representarse como
IntFunction<IntUnaryOperator>
.
Si expresa esto como sintaxis lambda no abreviada o sintaxis de clase anónima Java pre-lambda, es más claro lo que está sucediendo ...
La pregunta original.
¿Por qué son dos flechas?
Simple, hay dos funciones definidas ... La primera función es una función que define la función, la segunda es el resultado de esa función, que también es una función.
Cada uno requiere un operador
->
para definirlo.
No taquigrafía
IntFunction<IntUnaryOperator> curriedAdd = (a) -> {
return (b) -> {
return a + b;
};
};
Pre Lambda antes de 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;
}
};
Si observa
IntFunction<IntUnaryOperator>
podría aclararse:
IntFunction<R>
es una
IntFunction<R>
FunctionalInterface
.
Representa una función que toma un
int
y devuelve un valor de tipo
R
En este caso, el tipo de retorno
R
también es una
IntUnaryOperator
FunctionalInterface
, es decir, un
IntUnaryOperator
.
Entonces, la
primera función
(externa) en sí misma devuelve una función.
En este caso: cuando se aplica a un
int
, se supone que
curriedAdd
devuelve una función que nuevamente toma un
int
(y devuelve nuevamente
int
, porque eso es lo que hace
IntUnaryOperator
).
En la programación funcional es común escribir el tipo de una función como
param -> return_value
y ves exactamente eso aquí.
Entonces, el tipo de
curriedAdd
es
int -> int -> int
(o
int -> (int -> int)
si te gusta más).
La sintaxis lambda de Java 8 va junto con esto. Para definir dicha función, escribe
a -> b -> a + b
que es muy similar al cálculo lambda real:
λa λb a + b
λb a + b
es una función que toma un solo parámetro
b
y devuelve un valor (la suma).
λa λb a + b
es una función que acepta un solo parámetro
a
y devuelve otra función de un solo parámetro.
λa λb a + b
devuelve
λb a + b
con
a
conjunto al valor del parámetro.
Son dos expresiones lambda.
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
Una
IntFunction<R>
es una función
int -> R
IntUnaryOperator
es una función
int -> int
.
Por lo tanto, una
IntFunction<IntUnaryOperator>
es una función que toma un
int
como parámetro y devuelve una función que toma un
int
como parámetro y devuelve un
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
Quizás sea más claro si usa clases anónimas para "descomponer" la 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;
}
};
}
};