que-es-el-currying-en-javascript

Qué es el Currying en JavaScript

El currying es una técnica que transforma una función con múltiples parámetros en una secuencia de funciones, cada una con un solo parámetro.

En otras palabras, en lugar de pasar todos los argumentos a la función de una vez, el currying permite “descomponer” la función en pasos más simples.

El currying es un concepto proviene del ámbito matemático, y que ha encontrado un lugar importante en la programación funcional. En JavaScript, se ha vuelto un patrón muy popular.

Esto puede parecer complejo al principio. Pero, en en ocasiones, este enfoque permite mejorar la modularidad, la reutilización de código.

Currying en JavaScript

Para ilustrar el currying, supongamos un ejemplo sencillo. Imaginemos que tenemos una función simple que suma dos números:

function sumar(a, b) {
    return a + b;
}

¿Nada especialmente llamativo, verdad? Veamos la versión curried de esta funcion.

function sumarCurried(a) {
    return function(b) {
        return a + b;
    };
}

Ahora, en lugar de llamar a nuestra funcion sumar(a, b), tenemos una función que acepta un parámetro, y devuelve otra función que acepta otro parámetro.

Podemos utilizar nuestra versión sumarCurried de la siguiente manera:

const sumar5 = sumarCurried(5);

console.log(sumar5(3));  // Output: 8
console.log(sumar5(10)); // Output: 15

En este ejemplo,

  • sumarCurried toma un argumento a y devuelve una nueva función que espera el segundo argumento b.
  • Esto nos permite crear funciones especializadas (como sumar5) que siempre suman un número fijo.

Es decir, ahora tenemos una función que genera funciones que suman un número determinado.

Currying con lambdas

Podemos usar funciones lambda para crear una versión más compacta de currying. El comportamiento sigue siendo el mismo, pero el uso de lambdas lo hace más conciso y más acorde con el estilo moderno de JavaScript.

const sumar = a => b => a + b + c;

const resultado = sumar(2)(3); // Resultado: 5

En este ejemplo,

  • La función sumar ahora está definida como una serie de funciones anidadas utilizando flechas.
  • Cada función retorna otra función que acepta el siguiente argumento, hasta que se alcanza el cálculo final con a + b.

Implementación de Currying

Hemos visto como convertir una función en una versión “curried” de forma manual, en el ejemplo anterior con la función suma.

Sin embargo, lo normal es que hagamos alguna solución genérica, para convertir cualquier función. Vamos a ver alguna de ellas.

Función utilitaria

El currying se puede implementar con una función utilitaria sencilla. Por ejemplo

function currying(fn) {
    return function(arg1) {
        return function(arg2) {
            return fn(arg1, arg2);
        };
    };
}

// Uso
function multiplicar(a, b) {
    return a * b;
}

const multiplicarCurried = currying(multiplicar);
const multiplicarPor2 = multiplicarCurried(2);
console.log(multiplicarPor2(4)); // Output: 8

En este ejemplo, currying convierte una función de dos argumentos en una secuencia de funciones unarias.

Funcion utilitaria genérica

También podemos crear una función utilitaria para currying más avanzado, que un número arbitrario de argumentos.

function currying(fn) {
    return function curried(...args) {
        if (args.length >= fn.length) {
            return fn(...args);
        } else {
            return function(...args2) {
                return curried(...args, ...args2);
            };
        }
    };
}

// Uso
function multiplicar(a, b, c) {
    return a * b * c;
}

const multiplicarCurried = currying(multiplicar);
const multiplicarPor2y3 = multiplicarCurried(2)(3);
console.log(multiplicarPor2y3(4)); // Output: 24

En este ejemplo, la función currying toma cualquier función fn y devuelve una versión curried de ella. Esto permite que los argumentos se pasen uno a la vez o en grupos.

Uso de librerías

Muchas librerías y frameworks de JavaScript proporcionan soporte para currying. Por ejemplo, Lodash tiene una función _.curry que facilita la creación de funciones curried.

const _ = require('lodash');

const curriedSum = _.curry(function(a, b, c) {
    return a + b + c;
});

console.log(curriedSum(1)(2)(3)); // 6

Composición de funciones

El currying y la composición de funciones están estrechamente relacionados. Mientras que el currying descompone funciones en una serie de funciones unarias, la composición de funciones permite combinarlas para construir nuevas funciones más complejas.

const sumar = a => b => a + b;
const multiplicar = a => b => a * b;

// Combinamos las funciones para crear una nueva
const sumaMultiplicada = (a, b, c) => multiplicar(a)(sumar(b)(c));

console.log(sumaMultiplicada(2, 3, 4)); // Output: 14

En este ejemplo,

  • sumar y multiplicar son funciones curried que devuelven una nueva función al recibir un argumento.
  • sumaMultiplicada, utiliza composición. Primero, sumar(b)(c) calcula la suma de b y c, devolviendo 7. Luego, multiplicar(a)(7) multiplica el resultado por a, dando 14.

Es decir, hemos compuesto dos funciones curried (sumar y multiplicar) para formar una nueva función más compleja (sumaMultiplicada)

En cierto modo, la composición de funciones es el paso inverso al currying: en lugar de dividir, estamos combinando.

Ejemplos prácticos