Currying en Javascript

Adriel Zárate
4 min readJan 25, 2019

--

Currying es el proceso de descomponer una función que toma más de un parámetro en una serie de funciones que se invocan con los mismos argumentos, pero de manera parcial. El nombre viene de su aplicación en matemática.

// Función de suma sin curry, que toma tres parámetrosfunction suma(a, b, c) {
return a + b + c;
}
console.log( suma(1, 2, 3) ); // 6// En este ejemplo hacemos curry
// llamando a la función tres veces
// con un argumento a la vez
function sumaCurrying(a) {
return (b) => {
return (c) => {
return a + b + c;
};
};
}
console.log( sumaCurrying(1)(2)(3) ); // 6

Pero este ejemplo es demasiado específico. Si quisiéramos repetir la técnica en otros casos, para cada uno tendríamos que generar una nueva función que se resuelva en la cantidad correcta de invocaciones y con el número correcto de argumentos.

Otras opciones

Angus Croll nos enseña una implementación basada en la librería Prototype.js. Ésta consiste en agregar un método “curry” como prototipo de Function, habilitando la técnica para todas las funciones.

Modifiqué un poco el código para poder explicar mejor cómo funciona:

Function.prototype.curry = function() {    // si no hay arguments, retornamos la función original (this)    if (arguments.length < 1) return this;    // __method hace referencia a la función 
// que será "curryficada". En un primer momento
// será la la función add() que usamos más abajo,
// pero en la segunda llamada con curry() será
// la función anónima que retornamos en cada
// ejecución. Un log de this te lo demostrará.
var __method = this; // Array.prototype.slice.call se utiliza para convertir
// arguments en array y así luego poder utilizar concat,
// ya que arguments no dispone de dicho método
var args = Array.prototype.slice.call(arguments); // En cada invocación con .curry() se devuelve una función,
// que envuelve a la retornada anteriormente.
// Esta función anónima se ejecutará cuando ya no hagamos
// uso de .curry() y es donde el programa empieza
// el camino inverso al que hizo hasta aquí.
return function() { // all irá recogiendo en cada ejecución
// los argumentos anteriores y concatenándolos
// con los actuales.
var all = args.concat(Array.prototype.slice.call(arguments); // __method guarda una referencia al contexto superior,
// por lo que al invocarse irá subiendo y accediendo a
// los args anteriores y completando la variable all.
var result = __method.apply( this, all ); // cuando se termine de volver por todas las funciones
// se llega a la original ( add() en este caso )
// y se la ejecuta con todos los parámetros listos.
return result;
}
}// función sin curry
var add = function(a, b, c, d, e) {
return a + b + c + d + e;
}
var a = add.curry(1); // curry ya es parte del prototipo de Function
var b = a.curry(1);
var c = b.curry(1);
var d = c.curry(1);

console.log( d(1) ); // 5
var uno = add.curry(1, 1, 1); // se puede pasar más de un argumento!
var dos = uno.curry(1);
console.log( dos(1) );
// 5

Prototype.js

Con Prototype sólo hay que importar la librería en nuestro proyecto y podremos usar curry de la misma manera que en el ejemplo anterior:

<script src="/path/to/prototype.js" ></script>
<script>
var add = function(a, b, c, d, e) {
return a + b + c + d + e;
}
var uno = add.curry(1, 1, 1);
var dos = uno.curry(1);
console.log( dos(1) );
// 5
</script>

Lodash.js

Lodash no toca el prototipo de Function, por lo que nos obliga a crear una nueva versión de la función original con la ayuda de su método _.curry. Automáticamente, la librería crea la secuencia de return’s por nosotros.

<script src="/path/to/lodash.js" ></script>
<script>
var add = function(a, b, c, d, e) {
return a + b + c + d + e;
}
var addCurry = _.curry(add);
var dos = addCurry(1,1,1)(1);
console.log( dos(1) );

<script>

Para qué sirve curry?

Como hemos visto, la función original deja de ser un solo punto de entrada para los datos. Esto nos permite crear funciones configurables que pueden ser utilizadas en distintos puntos de nuestro programa hasta conseguir el resultado final. Un ejemplo sencillo con Prototype:

function saludo(a, b) => `Que tengas ${a}, ${b}.`;const saludoNoche = saludo.curry('buenas noches');// Que tengas buenas noches, hasta mañana.
console.log( saludoNoche('hasta mañana') );
// Que tengas buenas noches, dulces sueños.
console.log( saludoNoche('dulces sueños') );

Dudas aún? @mpjme tiene una muy buena explicación en su serie de videos sobre programación funcional en Javascript.

--

--