- A Receita Federal dos EUA disponibilizou em código aberto o novo Tax Withholding Estimator (TWE), e o princípio central do projeto é uma estrutura que modela a legislação tributária dos EUA como uma especificação declarativa baseada em XML
- A lógica de cálculo tributário do TWE é construída sobre um mecanismo chamado Fact Graph, que representa cada item tributário como um grafo de dependências de "fatos (Facts)" definidos em XML
- Implementar a lógica tributária em uma linguagem imperativa como JavaScript causa problemas como gerenciamento da ordem de execução, perda de valores intermediários e exposição de detalhes de implementação, por isso a abordagem declarativa é essencial
- JSON não é adequado para lidar com expressões aninhadas arbitrárias, e no XML a própria tag indica o tipo do objeto, o que o torna muito mais favorável para construir DSLs
- O XML permite aproveitar gratuitamente um ecossistema maduro de ferramentas, como XPath, tornando-se a opção mais econômica para especificações declarativas multiplataforma
Fact Graph: a legislação tributária dos EUA expressa em XML
- O Tax Withholding Estimator (TWE), divulgado pela IRS, é uma ferramenta que permite ao contribuinte informar renda e deduções para estimar impostos e retenções na fonte
- O projeto foi publicado como código aberto e também aceita contribuições do público
- O TWE é um site estático gerado a partir de duas configurações em XML, sendo a primeira um Fact Dictionary que representa a legislação tributária dos EUA
- O Fact Graph é um mecanismo de lógica originalmente criado para o projeto IRS Direct File e calcula a obrigação tributária e a retenção do contribuinte com base nos fatos definidos no Fact Dictionary
- Cada fato é definido em XML; por exemplo,
/totalOwed é expresso como um fato derivado (Derived) que subtrai /totalPayments de /totalTax
- O "valor total devido (total owed)" é a diferença entre o imposto total sobre a renda (
total tax) e o valor já pago (total payments)
- Créditos reembolsáveis (
refundable credits) são créditos tributários que podem tornar o saldo de imposto negativo; Earned Income Credit, Child Tax Credit e American Opportunity Credit são somados com <Add>
- Créditos não reembolsáveis (
non-refundable credits) só podem reduzir a carga tributária até 0; com o operador <GreaterOf>, escolhe-se o maior valor entre 0 e (imposto provisório - créditos não reembolsáveis)
- Os valores informados pelo usuário usam a tag
<Writable> em vez de <Derived>, e o tipo do valor é especificado com <Dollar/>, <Boolean/> etc.
- Os fatos dependem uns dos outros em uma estrutura de grafo que produz o valor final do imposto
Por que a lógica tributária precisa de especificações declarativas
- Se o mesmo cálculo fosse escrito em JavaScript,
const totalOwed = totalTax - totalPayments pareceria simples, mas isso é uma abordagem imperativa: a execução é sequencial e as etapas intermediárias se perdem
- Quando as dependências se aprofundam, surgem problemas de ordem de execução: uma função de entrada do usuário como
getInput() bloqueia todos os cálculos posteriores, e as próprias perguntas precisam mudar conforme a existência ou não de um cônjuge
- Na lógica de soma da renda do Social Security, detalhes de implementação em JavaScript como
map/reduce ficam expostos, enquanto <CollectionSum> em XML expressa o próprio conceito matemático tributário
<Dependency path="/socialSecuritySources/*/totalFederalTaxesPaid"/> soma os itens dentro da coleção
- O Fact Dictionary segue uma abordagem declarativa: basta descrever os cálculos nomeados e suas dependências, sem detalhar etapas concretas nem ordem de execução, e o mecanismo decide automaticamente como executar
- A vantagem mais importante do modelo tributário declarativo é a auditabilidade e a introspecção: é possível perguntar ao programa "como este número foi obtido?"
- Em programas imperativos, os valores intermediários já foram descartados e só podem ser verificados com logs ou depuradores; isso não escala em um sistema como a legislação tributária dos EUA, com centenas de cálculos intermediários
- Segundo Chris Given, autor original do Fact Graph, o Fact Graph é um meio de provar que itens não perguntados não alteraram o resultado da declaração e que todos os benefícios fiscais elegíveis foram considerados
- A Intuit, criadora do TurboTax, chegou à mesma conclusão e publicou em 2020 um white paper sobre o "Tax Knowledge Graph", mas a implementação não é pública
- Já o Fact Graph da IRS é código aberto e domínio público, podendo ser estudado, compartilhado e expandido por qualquer pessoa
Por que XML é muito mais adequado que JSON para DSLs
- Ao tentar usar JSON como formato declarativo para expressar a legislação tributária, lidar com expressões aninhadas arbitrárias se torna muito desconfortável
- Como a única estrutura de dados composta do JSON é o objeto, todo objeto filho precisa declarar seu tipo com algo como
"type", "kind" etc.
- No XML, o próprio nome da tag representa o tipo do objeto, então não é necessário declarar isso separadamente
- A representação em JSON do mesmo fato
/tentativeTaxNetNonRefundableCredits acaba sendo mais longa e complexa do que em XML
- XML oferece suporte a comentários e a um tratamento razoável de espaços e quebras de linha, sem as limitações incômodas que costumam ser tratadas como normais no JSON
- A combinação de atributos (
attribute) e elementos filhos nomeados (named children) oferece, no design da linguagem, expressividade para escolher o que enfatizar
- Também é possível definir tipos de dados próprios, como a distinção entre "dólar" e "inteiro"
- Ao lidar com textos explicativos longos, o XML é muito mais confortável para leitura e edição manual do que JSON
A generalidade do XML e seu ecossistema de ferramentas
- Sintaxes alternativas como S-expression, Prolog e KDL podem ser mais fáceis de ler que XML, mas ao usar XML você ganha de graça o parser e um ecossistema de ferramentas de uso geral
- S-expression funciona bem em Lisp, termos de Prolog funcionam bem em Prolog, mas XML pode ser convertido para qualquer formato
- Em Prolog, converter XML para um termo de Prolog pode ser feito com um único predicado
- O texto também menciona a pergunta do usuário ok123456 no Hacker News — "por que não usar Prolog/Datalog?" —; isso é possível, mas o XML leva vantagem em generalidade
- Sobre YAML, Chris Given afirma: "nunca tente expressar a lógica da legislação tributária dos EUA em YAML"
- Exemplo prático com XPath: foi criado um script que, com um único comando de shell, faz busca fuzzy por caminhos de fatos e consulta imediatamente a definição do caminho selecionado
cat facts.xml | xpath -q -e '//Fact/@path' | grep -o '/[^"]*' | fzf para buscar fatos
- Também foi adicionada a função de subir a cadeia de dependências para rastrear quais fatos dependem daquele fato
- Com cerca de 60 linhas de bash, isso evoluiu para uma ferramenta de depuração usada quase diariamente
- Outros membros da equipe também criaram suas próprias ferramentas rápidas de depuração, todas fazendo parse simples do XML e trabalhando em suas linguagens preferidas sem mexer na implementação Scala do Fact Graph
- A principal lição: representações genéricas de dados têm enorme valor, e nessa categoria existem apenas JSON e XML
- Na maioria dos casos deve-se escolher JSON, mas quando é preciso uma DSL, XML é a opção mais barata, e essa eficiência de custo permite que a equipe use o orçamento de inovação em outras áreas
Observações adicionais
- Mesmo pessoas que não programam conseguem ler XML se o esquema estiver bem projetado, embora seja desejável construir visualizações alternativas em separado
- O interesse por XML vem crescendo novamente: por exemplo, a ferramenta
grex, de Jake Low, que converte documentos XML para uma representação plana orientada a linhas, e o Xee, de Martijn Faassen, um motor moderno de XPath/XSLT implementado em Rust
- Como os fatos do TWE servem para estimar retenção na fonte, eles não devem ser usados diretamente para declaração de imposto
1 comentários
Comentários do Hacker News
XML é um formato caro de analisar corretamente em várias linguagens
Na prática, para implementar algo próximo do padrão, é preciso depender de três implementações open source como libxml2, expat e Xerces
O ponto central das linguagens da família SGML é tratar “listas” como objetos de primeira classe e aninhamento como objeto de segunda classe, além de permitir adicionar metadados em dois eixos: nomes de tags e atributos
XML ainda é útil como DSL, mas se for usar XML de verdade, a palavra “cheap” precisa ser descartada
Também é possível fazer uma DSL declarativa parecer uma expressão imperativa. Por exemplo, algo como
totalOwed = totalTax - totalPaymentspode ter o mesmo significado de uma DSL em XMLLinguagens como METAFONT mostram essa abordagem (link de exemplo)
Vejo XML repetindo os mesmos erros com frequência
Muita gente esquece a verdade simples de que quanto mais funcionalidades você coloca em um formato, mais difícil fica analisá-lo
JSON é popular porque tem poucos recursos e por isso é fácil de analisar
Já o XML colocou coisa demais: attributes, namespaces, CDATA, DTDs etc.
Também já houve discussões sobre usar SQLite como formato de intercâmbio, mas isso também corre o risco de ficar complexo como XML
É por isso também que CSV continua amado por ser simples
As tentativas atuais de enfiar comentários ou informação de tipos em JSON são uma recriação dos piores atributos do XML
Concordo, como autor do texto
Dá para fazer uma especificação declarativa parecer uma fórmula matemática, mas no fim isso significa criar uma nova linguagem
Aí surge o problema de portar o parser para todos os ambientes
Também é preciso decidir pessoalmente coisas sintáticas como precedência de operadores ou expressões switch, e a complexidade acaba explodindo
Foi exatamente por isso que usei a palavra “cheap” — usar um formato que já tem parser e tooling em todos os ambientes reduz custos
A expressividade diminui, mas para equipes pequenas é uma escolha sensata
Usei muito XML em Java enterprise, e ele era o principal culpado por gargalos de memória e CPU
XML não é cheap de jeito nenhum
O núcleo do SGML é o modelo de conteúdo baseado em expressões regulares dos elementos
Não é só uma estrutura de listas; ele também permite definir regras de produção gramaticais como em BNF
Dizer “XML proper” em vez de “XML lookalike” parece preciosismo demais
Mesmo sem usar todos os recursos do XML, ele continua sendo XML
É como chamar um ônibus escolar de “imitação de ônibus” só porque ele não tem porta-copos
Acho que basta usar uma linguagem com bom suporte a eDSL em vez de XML
Linguagens como Haskell, OCaml e Scala permitem expressar computação paralela com facilidade por meio de conceitos como applicative e arrow
Em JavaScript também dá para criar abstrações como
sumem vez de.reduce()Se você criar uma DSL em XML, acaba tendo que resolver de novo problemas como paralelização, legibilidade e inventar uma nova sintaxe
Em domínios complexos, há grande chance de esbarrar na décima lei de Greenspun
Mas linguagens como Haskell têm o problema de serem difíceis de aprender
Até desenvolvedores com 30 anos de carreira sentem que a barreira de entrada é alta
Raku também é uma boa opção
Começou com base em Haskell e oferece Grammar embutida e estilo funcional, o que favorece a escrita de DSLs
HTML! (resposta curta em tom de brincadeira)
Lisp também serve
Basta olhar para S-expressions para perceber o quanto XML parece verboso e pesado
Dá para projetar melhor a estrutura do JSON
Se cada nó for composto de uma chave de tipo e um valor em array, fica possível representá-lo como uma S-expression
Isso permite parsing em streaming e possibilita saber o tipo primeiro
É útil em datasets grandes
JSON é muito mais simples que XML e tem custo de parsing menor
No XML, casar tags e tratar atributos exige gerenciamento de estado mais complexo
No JSON, basta casar
{}e[]Essa simplicidade vai se acumulando e resulta em menor latência
Mas JSON tem aspas demais, a ponto de parecer ruído visual
Pessoalmente, acho o EDN do Clojure mais limpo
Esse tipo de estrutura em JSON parece, esteticamente, uma forma degenerada
Se os dados precisam de tags, talvez seja melhor usar uma forma de representação adequada a isso
O texto The Lost Art of XML me pareceu mais interessante
Achei marcante a visão de que boa parte das ferramentas de desenvolvimento web surgiu como consequência da derrota do XML nas browser wars
Mas acho difícil concordar com a afirmação de que “XML foi abandonado porque JavaScript venceu”
Os navegadores originalmente também suportavam XML (o X de AJAX é XML)
Os desenvolvedores é que simplesmente não gostavam de XML
Acho que ele foi rejeitado por causa de excesso de engenharia e complexidade
Tendo vivido diretamente a era das APIs em XML, XML era realmente doloroso
Era preciso criar encoder/decoder separado em cada linguagem, e a manutenção também era difícil
JSON mapeia simplesmente para arrays e objetos, então tem excelente compatibilidade entre linguagens
Quando lembro do tempo desperdiçado em reuniões de design de XML Schema, JSON simplificou o design de APIs como o Prettier acabou com a discussão de tabs vs spaces
No fim, tudo começa com a atitude de “não quero aprender algo complexo”, mas com o tempo os recursos voltam a fazer falta
A autoridade fiscal da Polônia ama XML
Mas o XML deles é tão obscuro que humanos não conseguem ler
Nomes de campos são coisas como
P_19N, e é preciso olhar o schema para entender o significado realHá até número de artigo de lei embutido
Ironicamente, o redator da lei do VAT hoje trabalha com consultoria tributária
Eu uso pessoalmente uma DSL baseada em S-expressions
Ela faz o papel de HTML e CSS em um runtime de navegador desktop baseado em WebAssembly,
e também foi reutilizada numa linguagem de marcação própria para resolver problemas de sincronização de documentos
Dá para ver exemplos relacionados no código de exemplo do CanvasUI, no arquivo de estilos e na ferramenta de documentação
Era marcante ver a reação dos candidatos no momento em que conseguiam implementar uma linguagem simples por conta própria
XML é menos uma DSL e mais uma ferramenta geral de parser/lexer
Ele apenas transforma texto em AST; a DSL real é a especificação definida em cima disso
Tem muitos recursos e é complexo, mas a vantagem é um ecossistema rico de tooling
É mais adequado para processar texto gerado do que para escrita manual
Como a validação de schema XSD já vem embutida, é possível verificar imediatamente a consistência do documento
Reclamar que XML é difícil sem usar ferramentas de automação é como lidar com binários sem disassembler
Mas a validação por schema, sozinha, não garante a correção do conteúdo
É o mesmo princípio de type checking não garantir a correção de um programa
XSD é útil, mas complexo e cheio de limitações
Por isso parte da comunidade XML migrou para RELAX-NG, embora ele não tenha conseguido substituí-lo completamente
Fico curioso sobre que tipo de trabalho realmente exige validação com XSD
XML é aceitável como linguagem de marcação e serve razoavelmente como formato de troca de dados, mas é terrível como linguagem de programação
JSON é parecido nesse ponto: é bom para intercâmbio de dados, mas se você tentar usá-lo como linguagem vai se arrepender
Linguagens baseadas em YAML, como Ansible, são exemplo disso
Já as S-expressions do Lisp têm estrutura parecida com a do JSON e mesmo assim evoluíram para uma linguagem excelente
O problema do XML não é tanto o XML em si, mas a dificuldade de gerar um XML bom
O padrão é complexo, e cada produtor representa as coisas de um jeito diferente, o que reduz a consistência
No JSON, essa variação é muito menor
Ver XML de instituições financeiras chega a dar desespero
O problema do XML, no fim, vinha da lentidão do tooling e de validadores incompletos
Mais do que a complexidade da representação dos dados, a qualidade das ferramentas era o verdadeiro gargalo
Na verdade, o problema não era “XML bom”, e sim que era fácil demais produzir XML horroroso
Por isso a comunidade tentou garantir interoperabilidade com namespaces, validação, transformação, web semântica etc.
Era um compromisso para conseguir tocar o trabalho adiante em ambientes onde consenso perfeito era impossível