Programación funcional en JavaScript: nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Programación funcional en JavaScript: de la teoría a la práctica en Node.js



La programación funcional (PF) es un paradigma que considera los cálculos como la evaluación de funciones matemáticas y evita cambiar el estado y los datos mutables. En el contexto de JavaScript y Node.js, la PF permite escribir código más predecible, testeable y fácil de mantener, especialmente al trabajar con flujos asíncronos y transformaciones complejas de datos. Este artículo está dirigido a desarrolladores que ya están familiarizados con los fundamentos de la PF y profundiza en conceptos avanzados: composición de funciones, functores, mónadas y gestión de efectos secundarios.



1. Funciones puras e inmutabilidad: el fundamento de la confiabilidad



A nivel avanzado, no basta con conocer la definición de una función pura. Es importante entender cómo la inmutabilidad y la ausencia de efectos secundarios afectan la arquitectura de la aplicación, especialmente en el entorno de Node.js, donde a menudo se utilizan operaciones concurrentes.



1.1. Copia profunda y uso compartido estructural (Structural Sharing)



La copia profunda completa de objetos grandes puede ser costosa. En su lugar, bibliotecas como Immer o Immutable.js utilizan el uso compartido estructural: al modificar una parte del objeto, se crea una nueva referencia, pero las partes no modificadas se comparten con el estado anterior.



// Ejemplo usando Immer (biblioteca popular)const { produce } = require('immer');

const baseState = { user: { name: 'Alice', age: 30 }, meta: { created: Date.now() }};

// produce crea un nuevo estado, sin mutar baseStateconst nextState = produce(baseState, draft => { draft.user.age = 31;});

console.log(baseState.user.age); // 30console.log(nextState.user.age); // 31console.log(baseState.meta === nextState.meta); // true (Structural Sharing)


1.2. Funciones puras y memoización



Las funciones puras son ideales para la memoización: almacenar en caché los resultados para evitar cálculos repetidos. En Node.js, esto es crítico para cálculos pesados o consultas a la base de datos.



// Memoización simple para una función puraconst memoize = (fn) => {  const cache = new Map();  return (...args) => {    const key = JSON.stringify(args);    if (cache.has(key)) {      console.log('Desde caché');      return cache.get(key);    }    const result = fn(...args);    cache.set(key, result);    return result;  };};

const fibonacci = (n) => (n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2));const memoizedFib = memoize(fibonacci);

console.log(memoizedFib(40)); // Se calculaconsole.log(memoizedFib(40)); // Desde caché


2. Composición de funciones: construir lo complejo a partir de lo simple



La composición es el proceso de combinar dos o más funciones para obtener una nueva función. A nivel avanzado, no solo usamos composición simple, sino también patrones como Pipe y Compose, que son la base para construir pipelines de procesamiento de datos.



2.1. Pipe y Compose



Pipe ejecuta las funciones de izquierda a derecha, mientras que Compose las ejecuta de derecha a izquierda. En Node.js, esto se usa a menudo para procesar datos entrantes (por ejemplo, solicitudes HTTP).



// Implementación de pipeconst pipe = (...fns) => (x) => fns.reduce((acc, fn) => fn(acc), x);

// Ejemplo: procesamiento de una cadenaconst toLowerCase = (str) => str.toLowerCase();const removeSpaces = (str) => str.replace(/\\s+/g, '-');const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);

const slugify = pipe(toLowerCase, removeSpaces, capitalize);console.log(slugify('Hello World From Node.js')); // Hello-world-from-node.js


2.2. Pipelines asíncronos



En aplicaciones reales de Node.js, las funciones suelen ser asíncronas. Creemos una versión de pipe para trabajar con promesas.



const asyncPipe = (...fns) => (x) =>  fns.reduce((acc, fn) => acc.then(fn), Pr

Blogs

Book Recommendations