preloader

Currificación en JavaScript

Currificación en JavaScript

La currificación es una técnica avanzada de manipulación de funciones que se usa en diversos lenguajes de programación, incluyendo JavaScript.

La currificación convierte una función que puede ser invocada como f(a, b, c) en una que se puede invocar como f(a)(b)(c).

La currificación no ejecuta la función, solo la transforma.

Vamos a ver un ejemplo para entender mejor el concepto y luego explorar sus aplicaciones prácticas.

Crearemos una función auxiliar curry(f) que currifica una función f de dos argumentos. Es decir, curry(f) transformará una función de la forma f(a, b) en una que se puede invocar como f(a)(b):

				
					function curry(f) { // curry(f) realiza la transformación de currificación
  return function(a) {
    return function(b) {
      return f(a, b);
    };
  };
}

// uso
function sum(a, b) {
  return a + b;
}

let curriedSum = curry(sum);

alert(curriedSum(1)(2)); // 3

				
			

Como se puede ver, la implementación es simple: dos funciones anidadas.

El resultado de curry(func) es una función que toma un argumento a. Cuando se invoca como curriedSum(1), el argumento se guarda y devuelve una nueva función que toma otro argumento b. Luego, esta función llama a la función original sum con los dos argumentos. Las implementaciones más sofisticadas de currificación, como _.curry de la biblioteca lodash, permiten invocar una función de manera normal o parcialmente:

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

let curriedSum = _.curry(sum); // usando _.curry de lodash

alert(curriedSum(1, 2)); // 3, invocación normal
alert(curriedSum(1)(2)); // 3, invocación parcial

				
			

¿Por qué usar currificación?

Para entender las ventajas, veamos un ejemplo práctico.

Supongamos que tenemos una función de registro log(date, importance, message) que formatea y muestra información. En proyectos reales, estas funciones pueden tener características útiles, como enviar registros a través de la red. Aquí, solo usaremos alert:

				
					function log(date, importance, message) {
  alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
}

				
			

Vamos a currificarla:

				
					log = _.curry(log);

				
			

Después de esto, log funciona normalmente:a

				
					log(new Date(), "DEBUG", "some debug"); // log(a, b, c)

				
			

…pero también se puede invocar de manera currificada:

				
					log(new Date())("DEBUG")("some debug"); // log(a)(b)(c)

				
			

Ahora podemos crear fácilmente una función para registrar eventos actuales:

				
					// logNow será una versión parcial de log con el primer argumento fijo
let logNow = log(new Date());

// uso
logNow("INFO", "message"); // [HH:mm] [INFO] message

				
			

Ahora logNow es log con el primer argumento fijo, es decir, una «función parcialmente aplicada» o «parcial» para abreviar.

Podemos ir más allá y crear una función conveniente para los registros de depuración actuales:

				
					let debugNow = logNow("DEBUG");

debugNow("message"); // [HH:mm] [DEBUG] message

				
			
  1. Así:

    • No perdemos la funcionalidad original: log todavía puede ser llamada normalmente.
    • Podemos crear fácilmente funciones parciales, como registros para el día de hoy.

    Implementación avanzada de currificación

    Para quienes quieran profundizar, aquí está la implementación de una currificación «avanzada» para funciones con múltiples argumentos.

    Es bastante breve:

				
					function curry(func) {
  return function curried(...args) {
    if (args.length >= func.length) {
      return func.apply(this, args);
    } else {
      return function(...args2) {
        return curried.apply(this, args.concat(args2));
      }
    }
  };
}

				
			

Ejemplos de uso:

				
					function sum(a, b, c) {
  return a + b + c;
}

let curriedSum = curry(sum);

alert(curriedSum(1, 2, 3)); // 6, llamada normal
alert(curriedSum(1)(2, 3)); // 6, currificación parcial
alert(curriedSum(1)(2)(3)); // 6, currificación completa

				
			

El nuevo curry puede parecer complicado, pero es fácil de entender.

El resultado de curry(func) es la función curried que se ve así:

				
					function curried(...args) {
  if (args.length >= func.length) { // (1)
    return func.apply(this, args);
  } else {
    return function(...args2) { // (2)
      return curried.apply(this, args.concat(args2));
    }
  }
}

				
			

Cuando se ejecuta, hay dos posibles rutas:

  1. Si el número de argumentos args es igual al que la función original func espera (func.length), se invoca la función original con func.apply.
  2. De lo contrario, se obtiene una función parcial: no se llama aún a func. En cambio, se devuelve otra función que aplicará curried con los argumentos anteriores junto con los nuevos.

Luego, en una nueva llamada, se obtiene otro parcial (si no hay suficientes argumentos) o finalmente el resultado.

Funciones con número fijo de argumentos

La currificación requiere que la función tenga un número fijo de argumentos.

Una función que utiliza parámetros variables, como f(...args), no puede ser currificada.

Más allá de la currificación

Por definición, la currificación debería convertir sum(a, b, c) en sum(a)(b)(c).

Pero la mayoría de las implementaciones de currificación en JavaScript son avanzadas, como se describió anteriormente: también permiten invocar la función de manera normal y devuelven un parcial si no se proporciona el número suficiente de argumentos.

Resumen

  • La currificación es una transformación que convierte f(a, b, c) en f(a)(b)(c).
  • Las implementaciones de currificación en JavaScript generalmente permiten invocar la función de manera normal y devuelven un parcial si no se proporcionan suficientes argumentos.
  • La currificación facilita la creación de funciones parciales. Como vimos en el ejemplo de registro, después de currificar la función universal de tres argumentos log(date, importance, message), podemos obtener parciales fácilmente cuando se llama con un argumento (como log(date)) o dos argumentos (como log(date, importance)).

Related Post

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *