Effect
Effect é uma biblioteca de TypeScript, mas a melhor forma de entrar nela não é pela lista de funções e sim por uma única ideia da qual todo o resto decorre: um programa deixa de ser executado na hora e passa a ser descrito como um valor. Você não chama o banco, não envia o e-mail, não faz o HTTP; você monta um objeto que sabe como fazer essas coisas quando alguém, mais tarde, mandar executá-lo. Tudo que a biblioteca oferece (tipagem forte, injeção de dependências, concorrência, retries, tracing) existe para tornar essa separação prática e segura.
Quem chega ao Effect ouvindo que ele é “lazy”, ou “monádico”, ou “programação funcional em TypeScript”, tende a colecionar rótulos sem o fio que os liga. O fio é esse: separar a descrição da execução. O resto são consequências.
O tipo que carrega tudo
Section titled “O tipo que carrega tudo”Um valor do Effect tem o tipo Effect<A, E, R>, e a leitura dele é a chave de leitura da biblioteca inteira:
“Eu descrevo um programa que, quando executado, pode produzir um valor
A, pode falhar com um erroE, e precisa de um ambienteRpara rodar.”
Três canais, portanto: o sucesso A, a falha E e as dependências R. É exatamente aí que o Effect se separa de uma Promise. Uma Promise<User> só te conta o caminho feliz; o erro é any (pode ser qualquer coisa, e o compilador não te obriga a tratar), e as dependências ficam escondidas dentro do corpo da função, invisíveis para quem chama. O Effect promove essas duas informações a cidadãs de primeira classe do tipo. O compilador passa a saber quais erros um programa pode ter e de quais serviços ele precisa, e cobra você por isso antes de rodar.
Por que descrever em vez de executar
Section titled “Por que descrever em vez de executar”Se o programa é um valor, e não um efeito que já aconteceu, então você pode manipulá-lo antes de ele rodar. É isso que compra as capacidades pelas quais o Effect é conhecido. Sobre uma descrição você adiciona retry, timeout, logs e tracing; troca as dependências por versões de teste; roda partes em paralelo; cancela; compõe programas menores em maiores. Nenhuma dessas operações executa nada. Todas apenas transformam a descrição, e o comportamento que você acrescentou vale para o programa inteiro, de forma uniforme, sem tocar na lógica de negócio que descreve o quê.
Essa é a inversão que reorganiza a arquitetura. Em vez de a aplicação ser uma sequência de chamadas que vão disparando efeitos conforme executam, ela vira um único valor que representa o programa completo, construído em tempo de composição e interpretado uma vez só, no ponto de entrada. As preocupações transversais deixam de ser espalhadas por todo canto e passam a ser camadas aplicadas sobre esse valor.
O mapa da seção
Section titled “O mapa da seção”O percurso vai do conceito que sustenta tudo até o vocabulário prático do dia a dia.
A descrição e a execução desenvolve a ideia central: por que o programa é um valor, por que o “lazy” é consequência e não causa, e como muitos Effects pequenos se compõem numa árvore de operações. É onde aparece a distinção entre os nós que apenas organizam o programa e as folhas onde o código de verdade mora, e por que o Effect é “contagioso”.
Generators e pipe trata dos dois estilos de escrever essa composição: o Effect.gen, que parece um async/await e descreve o fluxo interno do programa, e o pipe, que descreve as transformações aplicadas ao programa por fora. Um diz o que o programa faz; o outro, como ele será executado.
A borda e os erros responde onde os efeitos colaterais de fato acontecem (na borda, quando o runtime interpreta a descrição) e como as falhas são modeladas: como valores tipados, não como exceções soltas. É aqui que o try/catch da linguagem dá lugar a Effect.try e catchTag, e que a diferença entre sync e try deixa de ser detalhe.
Effect essencial é a virada para a prática: o menor subconjunto de APIs que cobre a maior parte do dia a dia, com uma forma idiomática escolhida por problema, para não se perder nas várias maneiras de fazer a mesma coisa que a biblioteca acumulou entre versões.
Layers e dependências fecha com a injeção de dependências, que no Effect não é uma biblioteca à parte: é o canal R do tipo, resolvido pelo compilador. Declarar um serviço, implementá-lo, compor o grafo e fornecê-lo ao programa é o ciclo que substitui o container de DI dos frameworks tradicionais.
Ao fim, a intenção é que “isso é só uma descrição, ainda não rodou” e “esse erro está no canal E, o compilador vai me cobrar” deixem de ser jargão e virem afirmações que você usa para pensar.
Palavras-chave
Section titled “Palavras-chave”Conceitos: descrição vs. execução, efeito como valor, laziness, três canais, tipagem estrutural, contágio, borda, concorrência estruturada
Tipo: Effect<A, E, R>, canal de sucesso, canal de erro, canal de dependências
Contraste: Promise, erro any, dependência implícita
Temas da seção: árvore de composição, generators e pipe, erros tipados, dialeto reduzido, Layers e DI