10 puntos por GN⁺ 2026-03-15 | 1 comentarios | Compartir por WhatsApp
  • El Servicio de Impuestos Internos de EE. UU. publicó como código abierto el nuevo Tax Withholding Estimator (TWE), y el principio clave de diseño es una estructura que modela la legislación fiscal estadounidense como una especificación declarativa basada en XML
  • La lógica de cálculo de impuestos de TWE está construida sobre un motor lógico llamado Fact Graph, que expresa cada concepto fiscal como un grafo de dependencias de "hechos" (Facts) definidos en XML
  • Si la lógica fiscal se implementa en un lenguaje imperativo como JavaScript, surgen problemas como la gestión del orden de ejecución, la pérdida de valores intermedios y la exposición de detalles de implementación, por lo que el enfoque declarativo resulta esencial
  • JSON no es adecuado para manejar expresiones anidadas arbitrarias, mientras que en XML las etiquetas mismas indican el tipo de objeto, así que es mucho más conveniente para construir un DSL
  • XML permite aprovechar gratuitamente un ecosistema maduro de herramientas como XPath, por lo que es la opción más rentable para especificaciones declarativas multiplataforma

Fact Graph: la legislación fiscal de EE. UU. expresada en XML

  • El Tax Withholding Estimator (TWE) publicado por el IRS es una herramienta que permite a los contribuyentes ingresar ingresos y deducciones para estimar impuestos y montos de retención
    • El proyecto está publicado como código abierto y también acepta contribuciones del público
  • TWE es un sitio estático generado a partir de dos configuraciones XML; la primera es el Fact Dictionary que representa la legislación fiscal estadounidense
  • Fact Graph es un motor lógico creado originalmente para el proyecto IRS Direct File, y calcula la obligación tributaria y las retenciones de un contribuyente con base en los hechos definidos en el Fact Dictionary
  • Cada hecho se define en XML; por ejemplo, /totalOwed se expresa como un hecho derivado (Derived) que resta /totalPayments de /totalTax
    • El "total adeudado (total owed)" es la diferencia entre el impuesto total sobre los ingresos (total tax) y el monto ya pagado (total payments)
  • Los créditos reembolsables (refundable credits) son créditos fiscales que pueden volver negativo el saldo de impuestos, y suman elementos como Earned Income Credit, Child Tax Credit y American Opportunity Credit con <Add>
  • Los créditos no reembolsables (non-refundable credits) solo pueden reducir la carga fiscal hasta 0, y usan el operador <GreaterOf> para elegir el mayor valor entre 0 y (impuesto tentativo - créditos no reembolsables)
  • Los valores ingresados por el usuario usan la etiqueta <Writable> en lugar de <Derived>, y especifican el tipo de valor con <Dollar/>, <Boolean/> y similares
  • Los hechos dependen entre sí en una estructura de grafo que produce la cifra fiscal final

Por qué la lógica fiscal necesita una especificación declarativa

  • Si el mismo cálculo se escribiera en JavaScript, sería breve como const totalOwed = totalTax - totalPayments, pero ese enfoque es imperativo, se ejecuta de forma secuencial y hace que se pierdan los pasos intermedios
  • Cuando las dependencias se profundizan, aparece el problema del orden de ejecución: una función de entrada del usuario como getInput() bloquea todos los cálculos posteriores, y las propias preguntas deben cambiar según si existe cónyuge, por ejemplo
  • En la lógica para sumar ingresos de Social Security quedan expuestos detalles de implementación de JavaScript como map/reduce, mientras que <CollectionSum> en XML expresa el concepto matemático fiscal en sí
    • Con <Dependency path="/socialSecuritySources/*/totalFederalTaxesPaid"/> se suman los elementos dentro de una colección
  • El Fact Dictionary sigue un enfoque declarativo, donde no se describen los pasos concretos de ejecución ni su orden; basta con describir cálculos con nombre y relaciones de dependencia, y el motor decide automáticamente cómo ejecutarlos
  • La ventaja más importante del modelo fiscal declarativo es la auditabilidad y la introspección interna: se le puede preguntar al programa "¿cómo llegaste a este número?"
    • En un programa imperativo, los valores intermedios ya se descartaron y solo pueden verse con registros o un depurador; esto no escala cuando hay cientos de cálculos intermedios, como en la legislación fiscal de EE. UU.
  • Según Chris Given, autor original de Fact Graph, Fact Graph es "un medio para demostrar que los elementos no preguntados no cambiaron el resultado de la declaración y que se están recibiendo todos los beneficios fiscales aplicables"
  • Intuit, creadora de TurboTax, llegó a la misma conclusión y publicó en 2020 un libro blanco sobre un "Tax Knowledge Graph", aunque su implementación no es pública
    • El Fact Graph del IRS es código abierto y de dominio público, por lo que cualquiera puede estudiarlo, compartirlo y ampliarlo

Por qué XML es mucho más adecuado que JSON para un DSL

  • Si se intenta usar JSON como formato de representación declarativa para la legislación fiscal, el manejo de expresiones arbitrariamente anidadas se vuelve muy incómodo
    • Como la única estructura de datos compuesta de JSON es el objeto, todos los objetos hijos tienen que indicar su tipo con algo como "type", "kind", etc.
    • En XML, el propio nombre de la etiqueta indica el tipo del objeto, así que no hace falta una declaración adicional
  • La representación en JSON del mismo hecho /tentativeTaxNetNonRefundableCredits termina siendo más larga y compleja que la de XML
  • XML admite comentarios (comments) y un manejo razonable de espacios y saltos de línea, evitando incomodidades que en JSON se dan por sentadas
  • Los atributos (attributes) y los elementos hijos con nombre (named children) ofrecen una capacidad expresiva que permite elegir qué enfatizar en el diseño del lenguaje
  • Es posible definir tipos de datos propios, como la distinción entre "dólar" y "entero"
  • Al tratar textos explicativos largos, XML es mucho más cómodo de leer y editar manualmente que JSON

La universalidad de XML y su ecosistema de herramientas

  • Sintaxis alternativas como S-expression, Prolog o KDL pueden resultar más legibles que XML, pero usar XML permite obtener gratis el parser y un ecosistema de herramientas de propósito general
    • Las S-expressions funcionan bien en Lisp y los términos de Prolog en Prolog, pero XML puede transformarse a cualquier formato
  • Convertir XML a términos de Prolog puede hacerse con un solo predicado (predicate)
  • También se menciona la pregunta del usuario de Hacker News ok123456 de si no bastaría usar "Prolog/Datalog"; sí es posible, pero XML tiene ventaja en universalidad
  • Sobre YAML, Chris Given comentó: "jamás intentes expresar la lógica de la legislación fiscal estadounidense en YAML"
  • XPath se usa en un caso práctico: se escribió un script que, con una sola línea de shell, busca de forma difusa rutas de hechos y consulta de inmediato la definición de la ruta seleccionada
    • cat facts.xml | xpath -q -e '//Fact/@path' | grep -o '/[^"]*' | fzf para buscar hechos
    • También se añadió una función para retroceder por la cadena de dependencias y rastrear qué hechos dependen de ese hecho
    • Con unas 60 líneas de bash, esto evolucionó a una herramienta de depuración que se usa casi a diario
  • Los integrantes del equipo también construyeron cada uno herramientas rápidas de depuración similares, todas parseando XML de forma trivial y trabajando en sus propios lenguajes sin tocar la implementación en Scala de Fact Graph
  • La lección central es que una representación de datos universal es muy valiosa, y en esta categoría solo existen JSON y XML
    • En la mayoría de los casos conviene elegir JSON, pero cuando se necesita un DSL, XML es la opción más barata, y esa eficiencia de costos permite al equipo usar su presupuesto de innovación en otras áreas

Información adicional

  • Incluso personas que no son programadoras pueden leer XML si el esquema está bien diseñado, aunque es recomendable construir vistas alternativas por separado
  • Últimamente está creciendo el interés por XML: grex, la herramienta de Jake Low para convertir documentos XML a una representación plana orientada a líneas, y Xee, el moderno motor XPath/XSLT implementado en Rust de Martijn Faassen, son algunos ejemplos
  • Los hechos de TWE son para estimar retenciones, por lo que no deben usarse directamente para presentar una declaración de impuestos

1 comentarios

 
GN⁺ 2026-03-15
Comentarios en Hacker News
  • XML es un formato costoso si quieres parsearlo correctamente en varios lenguajes
    En la práctica, para implementarlo de forma cercana al estándar hay que depender de tres implementaciones open source como libxml2, expat y Xerces
    La esencia de los lenguajes de la familia SGML es que tratan a las “listas” como objetos de primera clase y al anidamiento como objetos de segunda clase, y permiten agregar metadatos en dos ejes: nombres de etiquetas y atributos
    XML sigue siendo útil como DSL, pero si vas a usar XML de verdad, hay que dejar de lado la palabra “cheap”
    Además, un DSL declarativo también puede hacerse parecer una fórmula imperativa. Por ejemplo, una expresión como totalOwed = totalTax - totalPayments puede tener el mismo significado que un DSL en XML
    Lenguajes como METAFONT muestran este enfoque (enlace de ejemplo)

    • A menudo veo que XML repite el mismo error una y otra vez
      Mucha gente olvida la verdad simple de que cuantas más funciones le metes a un formato, más difícil es parsearlo
      JSON es popular porque tiene pocas funciones y por eso es fácil de parsear
      En cambio, XML metió demasiadas cosas: attributes, namespaces, CDATA, DTDs, etc.
      También hubo discusiones sobre usar SQLite como formato de intercambio, pero eso también corre el riesgo de volverse tan complejo como XML
      Esa es también la razón por la que CSV sigue siendo querido: por su simplicidad
      Hoy en día, los intentos de forzar comentarios o información de tipos dentro de JSON son una repetición de los malos atributos de XML

    • Como autor del texto, estoy de acuerdo
      Es posible hacer que una especificación declarativa se vea como una fórmula matemática, pero al final eso implica crear un lenguaje nuevo
      Y entonces aparece el problema de portar el parser a todos los entornos
      También hay que decidir por cuenta propia cosas sintácticas como la precedencia de operadores o expresiones switch, y al final la complejidad se dispara
      Por eso usé la palabra “cheap”: reduce costos usar un formato para el que ya existen parsers y tooling en todos los entornos
      La expresividad baja, pero para equipos pequeños es una decisión sensata

    • He usado mucho XML en Java empresarial, y era uno de los principales culpables de cuellos de botella de memoria y CPU
      XML no es cheap en absoluto

    • El núcleo de SGML es su modelo de contenido basado en expresiones regulares
      No se trata solo de estructuras de listas; también permite definir reglas de producción gramaticales como en BNF

    • Decir que no es “XML proper” sino un “XML lookalike” me parece una forma demasiado quisquillosa de expresarlo
      XML sigue siendo XML aunque no uses todas sus funciones
      Es como llamar “imitación de autobús” a un autobús escolar porque no tiene portavasos

  • Creo que en vez de XML basta con usar un lenguaje con buen soporte para eDSL
    Lenguajes como Haskell, OCaml y Scala permiten expresar cálculos paralelos fácilmente con conceptos como applicative o arrow
    En JavaScript también se puede crear una abstracción como sum en lugar de usar .reduce()
    Si haces un DSL en XML, al final tienes que volver a resolver problemas como paralelización, legibilidad y la invención de una sintaxis nueva
    En dominios complejos, es muy probable toparse con la décima ley de Greenspun

    • Pero lenguajes como Haskell tienen el problema de que son difíciles de aprender
      Incluso un desarrollador con 30 años de experiencia puede sentir que la barrera de entrada es alta

    • Raku también es una buena opción
      Comenzó con una base inspirada en Haskell y ofrece Grammar integrado y estilo funcional, lo que lo hace favorable para escribir DSL

    • ¡HTML! (respuesta corta en tono de broma)

    • Lisp también sirve
      Cuando ves S-expression, te das cuenta de inmediato de lo verboso y pesado que se siente XML

  • La estructura de JSON se puede diseñar mejor
    Si cada nodo se compone de una sola clave de tipo y un valor de arreglo, puede expresarse como un S-expression
    Así se vuelve posible el parsing en streaming y se puede conocer el tipo de antemano
    Eso resulta útil en conjuntos de datos grandes

    • JSON es mucho más simple que XML y tiene menor costo de parsing
      XML requiere manejo de estado complejo para emparejar etiquetas, procesar atributos, etc.
      En JSON basta con emparejar {} y []
      Esa simplicidad acumulada termina traduciéndose en menor latencia

    • Pero JSON tiene demasiadas comillas y se siente como ruido visual
      Personalmente, creo que EDN de Clojure se ve más limpio

    • Siento que ese tipo de estructura JSON es una forma degenerada desde lo estético
      Si los datos necesitan etiquetas, me parece mejor usar una forma de representación acorde a eso

  • El texto The Lost Art of XML me pareció más interesante
    Me llamó la atención la idea de que gran parte de las herramientas de desarrollo web surgieron como resultado de que XML perdió en las guerras del navegador

    • Pero me cuesta estar de acuerdo con la afirmación de que “XML fue descartado porque JavaScript ganó”
      Los navegadores originalmente también soportaban XML (la X de AJAX es XML)
      Lo que pasó fue que a los desarrolladores simplemente no les gustaba XML
      Creo que XML fue dejado de lado por su sobreingeniería y complejidad

    • Como alguien que vivió directamente la era de las API en XML, XML era realmente doloroso
      Había que crear encoders/decoders por separado en cada lenguaje, y mantenerlos también era difícil
      JSON simplemente se mapea a arreglos y objetos, así que la compatibilidad entre lenguajes es excelente
      Si pienso en el tiempo que se desperdiciaba en reuniones para diseñar esquemas XML, JSON simplificó el diseño de APIs igual que Prettier acabó con la discusión de tabs vs spaces

    • Al final, esto parte de la actitud de “no quiero aprender algo complejo”, pero con el tiempo vuelven a hacer falta más funciones

  • A la autoridad fiscal polaca le encanta XML
    Pero su XML es tan críptico que un humano no puede leerlo
    Los nombres de campo son cosas como P_19N, y hay que ver el esquema para entender su significado real
    Incluso incluyen números de artículos de ley
    Irónicamente, quien redactó la ley del IVA ahora trabaja en consultoría fiscal

  • Yo uso directamente un DSL basado en S-expression
    Cumple el papel de HTML y CSS en un runtime de navegador de escritorio basado en WebAssembly,
    y también lo reutilizo en un lenguaje de marcado propio que resuelve problemas de sincronización de documentos
    Se pueden ver ejemplos relacionados en el código de ejemplo de CanvasUI, el archivo de estilos y la herramienta de documentación

    • S-expression tiene un parser muy simple de implementar, al grado de que hasta lo usé como problema de entrevista
      La reacción de los candidatos cuando lograban implementar por sí mismos un lenguaje sencillo era memorable
  • XML, más que un DSL, es una herramienta general de parser/lexer
    Solo transforma texto en un AST; el DSL real es la especificación que defines encima
    Tiene muchas funciones y es complejo, pero tiene la ventaja de un ecosistema de tooling rico
    Es más adecuado para procesar texto generado que para escribirlo a mano

  • Como trae validación de esquemas XSD incorporada, se puede verificar de inmediato la consistencia del documento
    Quejarse de que XML es difícil sin usar herramientas de automatización es como manejar binarios sin desensamblador

    • Pero la validación de esquemas por sí sola no puede garantizar la corrección del contenido
      Es el mismo principio de que el chequeo de tipos no garantiza la corrección de un programa

    • XSD es útil, pero complejo y lleno de restricciones
      Por eso parte de la comunidad XML se pasó a RELAX-NG, aunque no logró reemplazarlo por completo

    • Me da curiosidad qué tipo de trabajo requiere necesariamente validación con XSD

  • XML está bien como lenguaje de marcado y es aceptable como formato de intercambio de datos, pero es terrible como lenguaje de programación
    Con JSON pasa algo parecido: es bueno para intercambio de datos, pero usarlo como lenguaje termina en arrepentimiento
    Los lenguajes basados en YAML, como Ansible, son un ejemplo de eso
    En cambio, los S-expression de Lisp tienen una estructura parecida a JSON y aun así evolucionaron hacia un gran lenguaje

  • El problema de XML no es tanto XML en sí, sino que es difícil generar buen XML
    El estándar es complejo y cada productor expresa las cosas de manera distinta, así que la consistencia se pierde
    En JSON esa desviación estándar es mucho menor
    Ver XML de instituciones financieras puede llegar a ser desmoralizante

    • El problema de XML, al final, era más bien la lentitud del tooling y los validadores incompletos
      Más que la complejidad de la representación de datos, el verdadero cuello de botella era la calidad de las herramientas

    • En realidad, el problema no era tanto “buen XML”, sino que era demasiado fácil crear XML desastroso
      Por eso la comunidad intentó asegurar interoperabilidad con namespaces, validación, transformación, web semántica, etc.
      Era una solución de compromiso para poder avanzar incluso en entornos donde un consenso perfecto era imposible