W3docs

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:

javascript— editable

Explicación:

  • La función add toma tres argumentos y devuelve su suma.
  • La función curryAdd es una versión con currying de add. Toma un argumento a y devuelve otra función que toma b. Esta segunda función devuelve otra función que toma c. La función más interna devuelve la suma de a, b y c.

Ventajas del Currying

  1. 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.
javascript— editable

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.

  1. 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.
javascript— editable

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:

javascript— editable

Explicación:

  1. Función de Currying (curry):

    • La función curry toma otra función fn como entrada.
    • Devuelve una nueva función llamada curried.
    • Esta función curried acepta cualquier número de argumentos usando la sintaxis de parámetros rest (...args).
  2. 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 original fn (fn.length).
    • Si se proporcionan suficientes argumentos, llama a la función original fn con esos argumentos usando fn.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 curried con los argumentos combinados (args.concat(nextArgs)), asegurando que todos los argumentos se recopilen antes de llamar a la función original fn.

    Nota: fn.length solo cuenta los parámetros sin valores predeterminados e ignora los parámetros rest.

  3. Ejemplo de Uso:

    • Definimos una función multiply que toma tres argumentos y devuelve su producto.
    • Creamos una versión con currying de la función multiply pasándola a la función curry, que devuelve una nueva función curriedMultiply.
    • Ahora, curriedMultiply puede llamarse con uno, dos o tres argumentos.
    • Cada vez que llamamos a curriedMultiply con 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.

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:

javascript— editable

Explicación:

  • La función formatMessage toma dos argumentos, greeting y name, y devuelve un mensaje formateado.
  • La función partial toma una función fn y algunos argumentos predefinidos (...presetArgs). Devuelve una nueva función que toma los argumentos restantes (...laterArgs).
  • Cuando se llama a la nueva función, combina presetArgs y laterArgs y llama a la función original fn con estos argumentos.
  • Usando partial, creamos greetHello, 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

  1. 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.

javascript— editable

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.

  1. 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.

javascript— editable

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.

  1. 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.

javascript— editable

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 luego g(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.

javascript— editable

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.length no es fiable para el auto-currying. Un helper genérico curry que depende de fn.length se 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 en fn.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 propio this, 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 el this dinámico, consulta el enlace de funciones.
Información

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.

javascript— editable

Explicación:

  • La función curriedMultiply se usa para crear multiplyByTwo, una función que multiplica su argumento por 2.
  • El array numbers se transforma usando map, aplicando multiplyByTwo a 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.

Práctica

Práctica
¿Qué es correcto sobre el Currying en JavaScript?
¿Qué es correcto sobre el Currying en JavaScript?
Was this page helpful?