
Programação funcional para Dart,
com laziness e concorrência embutidas.
FxDart é um port em Dart do FxTS — uma biblioteca para compor pipelines preguiçosos sobre dados síncronos e assíncronos, onde transformar seis requisições sequenciais de 1 segundo em um lote concorrente de 2 segundos é uma única chamada de método.
📒 Veja em ação: Daily Ledger — um app completo feito com FxDart →
⚖️ Dart vs FxDart — 53 tarefas reais, lado a lado →
⚡ RxDart vs FxDart — push vs pull, 50 vereditos honestos →
▲ Isto está ao vivo — edite o código e pressione Executar. Ele compila com o compilador Dart de verdade e executa no seu navegador.
O que é FxDart?
FxDart traz o modelo de programação do FxTS para Dart: um kit de ~120 funções pequenas e componíveis para transformar coleções e dados assíncronos. Três ideias o definem:
- Avaliação preguiçosa — operadores como
map,filteretakemontam um pipeline, mas não fazem trabalho algum até que um operador terminal (toList,each,reduce…) puxe valores por ele. Processar um range de um milhão de elementos com.take(3)calcula exatamente 3 resultados. - Um modelo para sync e async — os mesmos nomes de operadores
funcionam sobre
Iterables comuns e sobreFxAsyncIterable, a sequência assíncrona baseada em pull do FxDart (com pontes paraStreamnos dois sentidos). - Concorrência declarativa —
concurrent(n)pede ao pipeline upstream que avalienitens por vez, mantendo os resultados em ordem. É o recurso mais marcante do FxTS, portado fielmente para Dart.
Eventos no tempo, e falhas como valores, vivem nas outras duas superfícies. Qual superfície? é a decisão; esta página continua sendo a de marketing.
Por que precisamos disso?
Dart já tem Iterable e Stream. O FxDart conquista
seu espaço onde eles se esgotam:
| Problema | Dart puro | FxDart |
|---|---|---|
| Limitar chamadas de API concorrentes a n, mantendo a ordem | Filas manuais, Completers, controle minucioso |
.map(fetch).concurrent(3) |
| Pipelines de transformação assíncronos | Stream é baseado em push; misturar await, backpressure e laziness fica intrincado |
Cadeia baseada em pull — cada valor só é calculado quando pedido |
| Manipulação de dados (group, index, count, partition, zip, chunk…) | Loops escritos à mão toda vez | Uma função nomeada e bem testada por conceito |
| Transformações de várias etapas legíveis | Chamadas aninhadas ou variáveis intermediárias | Cadeias fx() da esquerda para a direita, totalmente tipadas |
Prós & Contras
✓ Prós
- Laziness de graça — pipelines fazem short-circuit; só os valores pedidos são calculados.
- Concorrência que preserva a ordem com um único operador:
concurrent(n)/concurrentPool(n)na ordem de conclusão. - Cadeias totalmente tipadas —
fx()mantém a inferência de ponta a ponta; operadores síncronos são funções comuns sobreIterables nativos, então tudo interopera com Dart comum. - Funções pequenas e focadas — ~120 operadores cobrindo transform / filter / slice / combine / aggregate / object / util.
- Semântica testada em produção — comportamento portado do FxTS junto com mais de 850 de seus testes.
- Zero dependências — Dart puro.
✗ Contras
- Sem currying data-last — Dart não tem sobrecarga de funções, então o estilo
pipecurried do FxTS vira cadeias; opipe()dinâmico perde os tipos estáticos. - Uma segunda abstração assíncrona —
FxAsyncIterableexiste porqueStreamnão consegue expressar o canal de retorno da concorrência; a ponte é fácil, mas é mais um conceito a aprender. - Curva de aprendizado — pensar em pipelines preguiçosos é diferente de loops imperativos.
- Nem sempre o mais rápido — em hot loops minúsculos, um
forescrito à mão pode superar a composição de operadores; o FxDart otimiza para clareza e trabalho I/O-bound. - Algumas APIs do TS não portam literalmente — elas ganham grafias nativas do Dart:
curryvira o getter de extensão tipado.curried, com os nomes antigos mantidos como stubs deprecados para migração.
Uma amostra de concorrência
Seis requisições falsas de 300 ms cada — sequencialmente ~1,8 s, com
concurrent(3) cerca de 0,6 s. Tente mudar o número: