
Programación funcional para Dart,
con pereza y concurrencia integradas.
FxDart es un port a Dart de FxTS: una librería para componer pipelines perezosos sobre datos síncronos y asíncronos, donde convertir seis peticiones secuenciales de 1 segundo en un lote concurrente de 2 segundos es una sola llamada a un método.
📒 Míralo en acción: Daily Ledger — una app completa hecha con FxDart →
⚖️ Dart vs FxDart — 53 tareas reales, lado a lado →
⚡ RxDart vs FxDart — push vs pull, 50 veredictos honestos →
▲ Esto está vivo: edita el código y pulsa Ejecutar. Se compila con el compilador real de Dart y se ejecuta en tu navegador.
¿Qué es FxDart?
FxDart lleva el modelo de programación de FxTS a Dart: un conjunto de ~120 funciones pequeñas y componibles para transformar colecciones y datos asíncronos. Lo definen tres ideas:
- Evaluación perezosa: operadores como
map,filterytakeconstruyen un pipeline pero no hacen nada hasta que un operador terminal (toList,each,reduce…) tira de los valores. Procesar un rango de un millón de elementos con.take(3)calcula exactamente 3 resultados. - Un solo modelo para sync y async: los mismos nombres de operadores
funcionan sobre
Iterables normales y sobreFxAsyncIterable, la secuencia asíncrona basada en pull de FxDart (con puentes aStreamen ambos sentidos). - Concurrencia declarativa:
concurrent(n)pide al pipeline aguas arriba que evalúenelementos a la vez manteniendo el orden de los resultados. Es la característica insignia de FxTS, portada fielmente a Dart.
Los eventos en el tiempo, y los fallos como valores, viven en las otras dos superficies. ¿Qué superficie? es la decisión; esta página sigue siendo la de marketing.
¿Por qué la necesitamos?
Dart ya tiene Iterable y Stream. FxDart se gana
su sitio donde estos se quedan cortos:
| Problema | Dart puro | FxDart |
|---|---|---|
| Limitar a n las llamadas concurrentes a una API, sin perder el orden | Colas manuales, Completers, control minucioso |
.map(fetch).concurrent(3) |
| Pipelines de transformación asíncronos | Stream se basa en push; mezclar await, contrapresión y pereza se complica |
Cadena basada en pull: cada valor se calcula solo cuando se pide |
| Manipulación de datos (agrupar, indexar, contar, particionar, zip, chunk…) | Bucles escritos a mano cada vez | Una función con nombre y bien probada por concepto |
| Transformaciones legibles de varios pasos | Llamadas anidadas o variables intermedias | Cadenas fx() de izquierda a derecha, totalmente tipadas |
Ventajas & desventajas
✓ Ventajas
- Pereza sin esfuerzo: los pipelines cortocircuitan; solo se calculan los valores solicitados.
- Concurrencia que preserva el orden con un solo operador:
concurrent(n)/concurrentPool(n)por orden de finalización. - Cadenas totalmente tipadas:
fx()mantiene la inferencia de principio a fin; los operadores síncronos son funciones normales sobreIterables nativos, así que todo interopera con Dart corriente. - Funciones pequeñas y específicas: ~120 operadores que cubren transformación / filtrado / segmentación / combinación / agregación / objeto / util.
- Semántica probada en producción: comportamiento portado desde FxTS junto con más de 850 de sus tests.
- Cero dependencias: Dart puro.
✗ Desventajas
- Sin currificación data-last: Dart no tiene sobrecarga de funciones, así que el estilo currificado con
pipede FxTS se convierte en cadenas; elpipe()dinámico pierde los tipos estáticos. - Una segunda abstracción asíncrona:
FxAsyncIterableexiste porqueStreamno puede expresar el canal de retorno de la concurrencia; el puente es fácil, pero es un concepto más que aprender. - Curva de aprendizaje: pensar en pipelines perezosos no es lo mismo que pensar en bucles imperativos.
- No siempre es lo más rápido: en bucles diminutos y muy calientes, un
forescrito a mano puede superar a la composición de operadores; FxDart optimiza la claridad y el trabajo limitado por E/S. - Algunas APIs de TS no se portan literalmente: adoptan una forma nativa de Dart; por ejemplo,
currypasa a ser el getter de extensión tipado.curried, y los nombres antiguos se conservan como stubs obsoletos para la migración.
Un aperitivo de concurrencia
Seis peticiones simuladas de 300 ms cada una: en secuencia ~1,8 s, con
concurrent(3) unos 0,6 s. Prueba a cambiar el número: