Currying y Aplicación Parcial en JavaScript
El currying y la aplicación parcial son técnicas de programación funcional que mejoran la modularidad y reutilización de tu código JavaScript.
El currying y la aplicación parcial son poderosas técnicas de programación funcional que pueden mejorar significativamente la modularidad y la reutilización de tu código JavaScript. Ambas se apoyan en los closures — la capacidad de una función interna de recordar las variables de la función externa que la creó — por lo que una función devuelta conserva los argumentos que ya has proporcionado.
Este artículo explica qué es el currying y por qué importa, cómo escribir un helper reutilizable curry, en qué se diferencia la aplicación parcial del currying, casos de uso del mundo real y las mejores prácticas (y errores comunes) a tener en cuenta.
Comprender el Currying
El currying es una técnica en la que una función se transforma en una secuencia de funciones, cada una tomando un único argumento. Permite descomponer una función que acepta múltiples argumentos en una serie de funciones unarias (de un solo argumento).
Ejemplo de Currying
Considera una función sencilla que suma tres números:
function add(a, b, c) {
return a + b + c;
}Podemos transformar esta función en una versión con currying:
Explicación:
- La función
addtoma tres argumentos y devuelve su suma. - La función
curryAddes una versión con currying deadd. Toma un argumentoay devuelve otra función que tomab. Esta segunda función devuelve otra función que tomac. La función más interna devuelve la suma dea,byc.
Ventajas del Currying
- Reutilización: Las funciones con currying permiten crear nuevas funciones fijando algunos argumentos. Esto mejora la reutilización al permitirte crear versiones especializadas de una función sin duplicar código.
Aquí, curriedAdd es una versión con currying de la función add. Nos permite crear funciones especializadas como add5 fijando el primer argumento (a) en 5. Esto promueve la reutilización del código, ya que podemos crear múltiples funciones especializadas sin repetir la lógica de suma.
- Composición de Funciones: El currying facilita la composición de funciones. La composición de funciones es el proceso de combinar dos o más funciones para producir una nueva función.
En este ejemplo, componemos las funciones multiply y addOne usando currying. La forma concisa a => b => a * b se basa en las arrow functions, que hacen que las funciones anidadas de un solo argumento sean fáciles de leer. Al aplicar currying a estas funciones, podemos crear fácilmente una nueva función addOneThenMultiplyBy5, que primero suma 1 a la entrada (x) y luego multiplica el resultado por 5. Esto demuestra cómo el currying facilita la composición de funciones, simplificando la creación de nuevas funciones combinando las existentes.
Implementar Currying en JavaScript
Podemos crear una función utilitaria para aplicar currying a cualquier función. Aquí hay una implementación de una función de currying genérica:
Explicación:
-
Función de Currying (
curry):- La función
currytoma otra funciónfncomo entrada. - Devuelve una nueva función llamada
curried. - Esta función
curriedacepta cualquier número de argumentos usando la sintaxis de parámetros rest (...args).
- La función
-
Función con Currying (
curried):- Dentro de
curried, se comprueba si el número de argumentos proporcionados (args.length) es mayor o igual al número de argumentos esperados por la función originalfn(fn.length). - Si se proporcionan suficientes argumentos, llama a la función original
fncon esos argumentos usandofn.apply(this, args). - Si no se proporcionan suficientes argumentos, devuelve una nueva función que acepta más argumentos (
nextArgs) usando el operador spread (...nextArgs). - Esta nueva función llama recursivamente a
curriedcon los argumentos combinados (args.concat(nextArgs)), asegurando que todos los argumentos se recopilen antes de llamar a la función originalfn.
Nota:
fn.lengthsolo cuenta los parámetros sin valores predeterminados e ignora los parámetros rest. - Dentro de
-
Ejemplo de Uso:
- Definimos una función
multiplyque toma tres argumentos y devuelve su producto. - Creamos una versión con currying de la función
multiplypasándola a la funcióncurry, que devuelve una nueva funcióncurriedMultiply. - Ahora,
curriedMultiplypuede llamarse con uno, dos o tres argumentos. - Cada vez que llamamos a
curriedMultiplycon un argumento o argumentos, devuelve una nueva función hasta que se recopilan todos los argumentos, momento en el que devuelve el resultado de multiplicar los argumentos.
- Definimos una función
Explorar la Aplicación Parcial
La aplicación parcial es una técnica en la que se crea una nueva función pre-llenando algunos argumentos de la función original. Esto es especialmente útil para crear funciones especializadas.
Ejemplo de Aplicación Parcial
Considera la siguiente función que formatea un mensaje:
function formatMessage(greeting, name) {
return `${greeting}, ${name}!`;
}Podemos crear una función de aplicación parcial:
Explicación:
- La función
formatMessagetoma dos argumentos,greetingyname, y devuelve un mensaje formateado. - La función
partialtoma una funciónfny algunos argumentos predefinidos (...presetArgs). Devuelve una nueva función que toma los argumentos restantes (...laterArgs). - Cuando se llama a la nueva función, combina
presetArgsylaterArgsy llama a la función originalfncon estos argumentos. - Usando
partial, creamosgreetHello, una función que siempre usa "Hello" como saludo. Cuando se llama con un nombre, devuelve el mensaje completo.
Ventajas de la Aplicación Parcial
-
Simplificación: Crear funciones más simples a partir de otras más complejas
Supongamos que tenemos una función que calcula el precio final de un artículo después de aplicar un descuento e impuestos.
function calculateFinalPrice(price, discount, tax) {
return price - (price * discount) + (price * tax);
}Esta función requiere tres argumentos, lo que la hace un poco engorrosa de usar repetidamente si el descuento y las tasas de impuestos son a menudo los mismos. Con la aplicación parcial, podemos simplificar esto.
En el ejemplo anterior, applyDiscountAndTax es una función de aplicación parcial que preestablece los valores de discount y tax. Esto hace más fácil calcular el precio final para diferentes artículos sin especificar repetidamente el descuento y las tasas de impuestos.
-
Reutilización de Código: Reutilizar la lógica común de una función con diferentes argumentos predefinidos
Imagina que tenemos una función que registra mensajes con diferentes niveles de severidad.
function logMessage(level, message) {
console.log(`[${level}] ${message}`);
}Podemos crear funciones reutilizables para diferentes niveles de registro usando la aplicación parcial.
Aquí, createLogger es una función de aplicación parcial que establece el argumento level. Las funciones infoLogger y errorLogger ahora pueden usarse para registrar mensajes con los niveles de registro predefinidos, reutilizando la lógica común de logMessage.
- Mejor Legibilidad: Hace el código más legible al descomponer funciones complejas Considera una función que formatea fechas en diferentes estilos.
function formatDate(date, format) {
const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
if (format === 'US') {
options.month = 'long';
} else if (format === 'EU') {
options.day = 'numeric';
options.month = 'numeric';
}
return new Date(date).toLocaleDateString(undefined, options);
}Usando la aplicación parcial, podemos crear funciones más legibles para diferentes formatos de fecha.
createDateFormatter aplica parcialmente el argumento format, resultando en funciones específicas para los formatos de fecha de EE.UU. y Europa. Esta descomposición hace el código más legible y fácil de entender, ya que cada función formateadora está dedicada a un formato particular.
Estos ejemplos ilustran cómo la aplicación parcial en JavaScript puede simplificar funciones complejas, mejorar la reutilización del código y aumentar la legibilidad, haciendo el código más fácil de gestionar y comprender.
Currying vs. Aplicación Parcial
Estas dos técnicas están estrechamente relacionadas y a menudo se confunden, pero no son lo mismo:
- Currying transforma una función de N argumentos en N funciones anidadas que toman exactamente uno por llamada. Una función completamente currificada siempre se llama un argumento a la vez:
f(a)(b)(c). - Aplicación parcial fija algunos argumentos de una función y devuelve una nueva función que acepta el resto en una sola llamada:
g = partial(f, a)y luegog(b, c).
En resumen: el currying trata sobre la forma de la función (una cadena de llamadas unarias), mientras que la aplicación parcial trata sobre pre-llenar argumentos. Ten en cuenta que el helper genérico curry anterior es más flexible que el currying estricto — acepta argumentos en grupos (curriedMultiply(2, 3)(4)), por lo que difumina la línea y efectivamente también admite la aplicación parcial.
Mejores Prácticas para Usar Currying y Aplicación Parcial
Mantener las Funciones Puras
- Asegúrate de que las funciones con currying y las de aplicación parcial permanezcan puras, sin efectos secundarios. Esto las hace más fáciles de razonar y probar.
Usar Cuando Corresponda
- Usa el currying y la aplicación parcial cuando encajen de forma natural con el problema en cuestión.
- Evita el uso excesivo de estas técnicas, ya que pueden dificultar la comprensión del código si no se utilizan con criterio.
Aprovechar la Composición de Funciones
- Combina funciones con currying para crear funcionalidad más compleja. El currying funciona bien con la composición de funciones, dando lugar a un código más modular.
Documentación y Nomenclatura
- Documenta adecuadamente las funciones con currying y de aplicación parcial para indicar su uso esperado.
- Usa nombres claros y descriptivos para las funciones que transmitan su propósito.
Presta Atención a Estos Errores Comunes
fn.lengthno es fiable para el auto-currying. Un helper genéricocurryque depende defn.lengthse comportará incorrectamente con parámetros predeterminados o parámetros rest, porque estos no se cuentan. Si una función los usa, pasa la aridad esperada explícitamente en lugar de confiar enfn.length.- El exceso de currying perjudica la legibilidad. Una cadena larga
f(a)(b)(c)(d)es más difícil de leer que una sola llamada con un buen nombre. Recurre al currying cuando realmente elimine repetición. - Cuidado con
this. Las arrow functions no tienen su propiothis, por lo que una cadena con currying escrita con arrow functions no puede usarse como método que depende del objeto que la llama. Si necesitas elthisdinámico, consulta el enlace de funciones.
Si usas con frecuencia el currying y la aplicación parcial, considera usar la librería Lodash, que proporciona funciones utilitarias convenientes como _.curry y _.partial.
Combinar con Métodos de Array
El currying puede combinarse eficazmente con los métodos de array como map, filter y reduce para obtener un código conciso y expresivo.
Explicación:
- La función
curriedMultiplyse usa para crearmultiplyByTwo, una función que multiplica su argumento por 2. - El array
numbersse transforma usandomap, aplicandomultiplyByTwoa cada elemento, lo que resulta en un nuevo array de números duplicados.
Conclusión
El currying y la aplicación parcial en JavaScript ofrecen poderosas técnicas para simplificar la composición de funciones, mejorar la reutilización del código y aumentar la legibilidad. El currying transforma funciones con múltiples argumentos en una serie de funciones unarias, permitiendo un código más flexible y modular. La aplicación parcial permite preestablecer los argumentos de una función, facilitando la reutilización del código y la simplificación de funciones complejas. Al aprovechar estos conceptos de programación funcional, los desarrolladores pueden escribir código JavaScript más limpio, conciso y mantenible.