cover

Introducción rápida a Effect (TS)


Escuchar este post

Selecciona una voz y genera audio para escuchar este post

Una guía rápida para principiantes que quieren dominar el manejo de efectos en TypeScript ✅


¿Qué es Effect y por qué debería importarte?

Si has trabajado con TypeScript durante un tiempo, probablemente te has enfrentado a algunos desafíos comunes: manejar errores de manera elegante, trabajar con operaciones asíncronas complejas, o simplemente escribir código que sea tanto robusto como fácil de mantener. Effect es una biblioteca revolucionaria que transforma la manera en que abordamos estos problemas. 🤓

Effect no es solo otra biblioteca de utilidades. Es un sistema completo para manejar efectos secundarios de manera funcional, proporcionando herramientas poderosas para crear aplicaciones más confiables y predecibles.

¿Qué son los "Efectos"?

Antes de sumergirnos en Effect, entendamos qué son los efectos en programación:

// ❌ Función pura - sin efectos function sumar(a: number, b: number): number { return a + b; } // ⚠️ Función con efectos - puede fallar, es asíncrona async function obtenerUsuario(id: string): Promise<Usuario> { const response = await fetch(`/api/users/${id}`); return response.json(); }

Los efectos son operaciones que pueden:

  • Fallar (errores)
  • Ser asíncronas (promesas)
  • Depender del contexto (configuración, base de datos)
  • Tener efectos secundarios (logs, mutaciones)

Tu Primera Experiencia con Effect

Instalación

npm install effect

El Tipo Effect

El corazón de Effect es el tipo Effect<Success, Error, Requirements>:

import { Effect } from "effect"; // Effect que puede: // - Retornar un string (Success) // - Fallar con Error (Error) // - No necesita dependencias (never) const saludo: Effect.Effect<string, Error, never> = Effect.succeed("¡Hola, Effect!");

Ejemplo Práctico: API de Usuario

Veamos cómo Effect transforma el código típico de manejo de APIs:

Antes (JavaScript/TypeScript tradicional):

// ❌ Código tradicional - propenso a errores async function obtenerPerfilUsuario(id: string) { try { const response = await fetch(`/api/users/${id}`); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const usuario = await response.json(); if (!usuario.email) { throw new Error("Usuario sin email"); } return { nombre: usuario.name, email: usuario.email, activo: usuario.active ?? false }; } catch (error) { console.error("Error obteniendo usuario:", error); throw error; } }

Después (con Effect):

import { Effect, pipe } from "effect"; // ✅ Código con Effect - robusto y expresivo const obtenerPerfilUsuario = (id: string) => pipe( Effect.tryPromise({ try: () => fetch(`/api/users/${id}`), catch: (error) => new FetchError(error) }), Effect.flatMap(response => response.ok ? Effect.tryPromise({ try: () => response.json(), catch: (error) => new ParseError(error) }) : Effect.fail(new HttpError(response.status)) ), Effect.flatMap(usuario => usuario.email ? Effect.succeed({ nombre: usuario.name, email: usuario.email, activo: usuario.active ?? false }) : Effect.fail(new ValidationError("Usuario sin email")) ) );

Conceptos Fundamentales

1. Creación de Effects

// Éxito inmediato const exito = Effect.succeed(42); // Fallo inmediato const fallo = Effect.fail(new Error("Algo salió mal")); // Effect que puede fallar const division = (a: number, b: number) => b === 0 ? Effect.fail(new Error("División por cero")) : Effect.succeed(a / b);

2. Composición con pipe

import { pipe } from "effect"; const procesarNumero = (n: number) => pipe( Effect.succeed(n), Effect.map(x => x * 2), // Transformar el valor Effect.flatMap(x => // Encadenar otro Effect x > 10 ? Effect.succeed(`Grande: ${x}`) : Effect.fail(new Error("Muy pequeño")) ) );

3. Manejo de Errores Elegante

const operacionSegura = pipe( operacionPeligrosa(), Effect.catchAll(error => { console.log("Error capturado:", error); return Effect.succeed("Valor por defecto"); }) );

Ventajas de Effect sobre Promesas

| Aspecto | Promesas | Effect | | --- | --- | --- | | Tipado de errores | ❌ any en catch | ✅ Errores tipados | | Composición | ⚠️ async/await | ✅ Pipe operator | | Lazy evaluation | ❌ Se ejecutan inmediatamente | ✅ Solo cuando se ejecutan | | Cancelación | ⚠️ Limitada | ✅ Cancelación completa | | Dependencias | ❌ No hay sistema | ✅ Sistema de dependencias |

Ejemplo del Mundo Real: Sistema de Notificaciones

import { Effect, pipe } from "effect"; // Definir errores específicos class EmailError extends Error { readonly _tag = "EmailError"; } class SMSError extends Error { readonly _tag = "SMSError"; } // Servicios const enviarEmail = (email: string, mensaje: string) => Effect.tryPromise({ try: () => emailService.send(email, mensaje), catch: () => new EmailError("Fallo al enviar email") }); const enviarSMS = (telefono: string, mensaje: string) => Effect.tryPromise({ try: () => smsService.send(telefono, mensaje), catch: () => new SMSError("Fallo al enviar SMS") }); // Lógica de negocio const notificarUsuario = (usuario: Usuario, mensaje: string) => pipe( // Intentar email primero enviarEmail(usuario.email, mensaje), Effect.catchTag("EmailError", () => // Si falla, intentar SMS enviarSMS(usuario.telefono, mensaje) ), Effect.catchTag("SMSError", () => // Si ambos fallan, log y continuar Effect.sync(() => { console.log(`No se pudo notificar a ${usuario.nombre}`); return "Notificación fallida"; }) ) );

Ejecutando Effects

Los Effects son lazy - no hacen nada hasta que los ejecutas:

import { Effect } from "effect"; // Crear el Effect (no se ejecuta) const miEffect = Effect.succeed("¡Hola!"); // Ejecutarlo Effect.runPromise(miEffect).then(console.log); // "¡Hola!" // O con manejo de errores Effect.runPromiseExit(miEffect).then(result => { if (result._tag === "Success") { console.log("Éxito:", result.value); } else { console.log("Error:", result.cause); } });

Próximos Pasos

Effect es un ecosistema rico con muchas características avanzadas:

  • Schema: Validación y transformación de datos
  • Stream: Procesamiento de flujos de datos
  • Fiber: Concurrencia estructurada
  • Layer: Sistema de dependencias
  • STM: Memoria transaccional

Enlaces relacionados

  1. Documentación oficial de Effect
  2. Effect Discord Community
  3. Ejemplos en GitHub

Effect representa un cambio de paradigma en cómo escribimos código TypeScript. Al adoptar principios de programación funcional y proporcionar herramientas poderosas para el manejo de efectos, Effect nos permite crear aplicaciones más robustas, mantenibles y predecibles. 🛩️

No necesitas reescribir toda tu aplicación de una vez. Puedes empezar adoptando Effect gradualmente en las partes más críticas de tu código, especialmente donde manejas operaciones asíncronas complejas o necesitas un mejor control de errores.

¿Listo para comenzar tu viaje con Effect? El futuro de la programación funcional en TypeScript te espera. 🧙🏼‍♂️


¿Te gustó este artículo? Compártelo con tu equipo y ayuda a más desarrolladores a descubrir el poder de Effect.

Abrazo. Bliss. 🤓

meta cover

Construye tu propio useOutsideClick

Checa este otro Post

meta cover

¿Qué son las Future Flags?

Checa este otro Post

¡Nuevo curso!

Animaciones web con React + Motion 🧙🏻