
Escuchar este post
Selecciona una voz y genera audio para escuchar este post
Introducción
Cuando trabajamos con programación funcional en TypeScript, especialmente con bibliotecas como Effect, a menudo necesitamos realizar efectos secundarios (como logging o depuración) sin alterar el flujo de nuestros datos. Aquí es donde Effect.tap se convierte en nuestra herramienta de confianza. 🔪
¿Qué es Effect.tap?
Effect.tap es una función que nos permite ejecutar un efecto secundario con el valor actual de nuestro flujo, sin modificar dicho valor. Es como echar un vistazo a los datos mientras pasan por nuestra cadena de operaciones.
Analogía del Restaurante
Imagina que eres un chef en una cocina:
- map: Transformas los ingredientes (cortar, cocinar, mezclar) 🥣
- tap: Pruebas la comida para asegurarte de que todo va bien, pero no la modificas 👩🏻🍳
Ejemplo Básico
import { Effect, pipe } from "effect"; // Sin tap const programa1 = pipe( Effect.succeed(5), Effect.map(x => { console.log("El valor es:", x); return x * 2; // ¡Tengo que recordar devolver el valor! }) ); // Con tap (más limpio y seguro) const programa2 = pipe( Effect.succeed(5), Effect.tap(x => Effect.sync(() => console.log("El valor es:", x)) ), Effect.map(x => x * 2) // El valor original sigue siendo 5 aquí );
Casos de Uso Comunes
1. Logging para Depuración
import { Effect, pipe } from "effect"; function procesarUsuario(id: number) { return pipe( obtenerUsuario(id), Effect.tap(usuario => Effect.sync(() => console.log("Usuario obtenido:", usuario)) ), Effect.flatMap(validarUsuario), Effect.tap(usuarioValido => Effect.sync(() => console.log("Usuario validado:", usuarioValido)) ) // El flujo continúa con el usuario validado ); }
2. Monitoreo de Rendimiento
import { Effect, pipe } from "effect"; function conTiempoDeEjecucion<E, A>(efecto: Effect.Effect<A, E>) { const inicio = Date.now(); return pipe( efecto, Effect.tap(() => Effect.sync(() => { const fin = Date.now(); console.log(`Tiempo de ejecución: ${fin - inicio}ms`); }) ) ); } // Uso const resultado = await Effect.runPromise( conTiempoDeEjecucion( Effect.sync(() => { // Código que queremos medir let suma = 0; for (let i = 0; i < 1000000; i++) { suma += i; } return suma; }) ) );
Diferencias Clave
tap vs map
| Característica | tap | map |
| --- | --- | --- |
| Modifica el valor | ❌ No | ✅ Sí |
| Retorna | El mismo valor | Un nuevo valor |
| Uso típico | Efectos secundarios | Transformaciones |
tap vs flatMap
- tapes como- flatMappero descarta el resultado de la función
- tap(fn)es equivalente a- flatMap(x => fn(x).pipe(map(() => x)))
Buenas Prácticas
- Mantén los efectos secundarios pequeños: tapes para operaciones rápidas como logging.
- No modifiques el estado global: tapno debería tener efectos colaterales observables.
- Usa Effect.syncoEffect.promisepara envolver operaciones síncronas o asíncronas.
- Sé explícito: Si necesitas transformar el valor, usa mapoflatMapen su lugar.
Ejemplo Avanzado: Pipeline de Procesamiento
import { Effect, pipe } from "effect"; // Tipos de dominio type Producto = { id: number; nombre: string; precio: number; stock: number; }; // Función simulada para obtener un producto function obtenerProducto(id: number): Effect.Effect<Producto> { return Effect.succeed({ id, nombre: "Laptop", precio: 1200, stock: 10 }); } // Pipeline de procesamiento const procesarPedido = (productoId: number, cantidad: number) => pipe( // 1. Obtener el producto obtenerProducto(productoId), // 2. Loggear el producto obtenido Effect.tap(producto => Effect.sync(() => console.log(`Procesando: ${producto.nombre} (${cantidad} unidades)`) ) ), // 3. Validar stock Effect.flatMap(producto => { if (producto.stock < cantidad) { return Effect.fail(new Error("Stock insuficiente")); } return Effect.succeed(producto); }), // 4. Calcular total (y loggear) Effect.tap(producto => { const total = producto.precio * cantidad; return Effect.sync(() => console.log(`Total a cobrar: $${total}`) ); }), // 5. Retornar el producto (sin modificar) Effect.map(producto => ({ ...producto, stock: producto.stock - cantidad })), // 6. Manejo de errores Effect.catchAll(error => Effect.sync(() => { console.error("Error en el pedido:", error.message); return null; }) ) ); // Ejecutar el pipeline Effect.runPromise(procesarPedido(1, 2));
Conclusión
Effect.tap es una herramienta poderosa que nos permite:
- Mantener la inmutabilidad de nuestros datos
- Depurar fácilmente nuestros pipelines
- Agregar lógica de observabilidad sin ensuciar nuestra lógica de negocio
- Mantener el código limpio y expresivo
Recuerda: si necesitas transformar datos, usa map o flatMap. Si solo necesitas "ver" los datos de paso, tap es tu mejor opción.
¿Listo para implementar Effect.tap en tus proyectos? ¡Tu código te lo agradecerá! 🚀
Abrazo. Bliss. 🤓
Enlaces relacionados

Tu Primer Proyecto Web en 1 Hora: Iniciando con Elixir y un Asistente de IA
Checa este otro Post

